<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Andrew Loutfi — writing</title>
    <link>https://aloutfi.com</link>
    <atom:link href="https://aloutfi.com/rss.xml" rel="self" type="application/rss+xml"/>
    <description>AI engineer building infrastructure tools. Writing on backend and platform engineering.</description>
    <language>en-us</language>
    <item>
      <title>[SAMPLE] Draw your Terraform state boundaries before they draw themselves</title>
      <link>https://aloutfi.com/writing/sample-terraform-state-boundaries</link>
      <guid isPermaLink="true">https://aloutfi.com/writing/sample-terraform-state-boundaries</guid>
      <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
      <description>State files are blast radii. A sample post with headings, code, and a table to verify typography — replace with real writing.</description>
      <content:encoded><![CDATA[<p><em>This is a sample post shipped with the site scaffold so the typography can be
checked against real-shaped content. Delete it before launch.</em></p>
<p>Nobody plans a 4,000-resource Terraform state. It accretes: the network module
lands next to the database because that's where the file was open, someone adds
DNS "temporarily," and eighteen months later a plan takes eleven minutes and
touching a tag forces a full-stack review.</p>
<h2>State is a blast radius, not a folder</h2>
<p>Every resource in a state file shares fate with every other resource in it. A
bad refactor, a provider bug, a mistyped <code>terraform state rm</code> — the damage is
scoped to the file. That makes the state boundary the most important
architectural decision in an IaC codebase, and it's usually made by accident.</p>
<p>Three questions locate a sane boundary:</p>
<ol>
<li><strong>Change cadence</strong> — does this change weekly or yearly? Networking and DNS
move slowly; application infrastructure moves constantly. Separate them.</li>
<li><strong>Failure tolerance</strong> — if this state were corrupted, what's the recovery
story? Keep hard-to-rebuild things (stateful stores, zones) away from
things you'd happily recreate.</li>
<li><strong>Ownership</strong> — can one team apply this without paging another? If not,
the file is a meeting disguised as infrastructure.</li>
</ol>
<h2>A shape that survives growth</h2>
<table>
<thead>
<tr>
<th>State</th>
<th>Contents</th>
<th>Cadence</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>foundation</code></td>
<td>VPCs, subnets, DNS zones</td>
<td>quarterly</td>
</tr>
<tr>
<td><code>platform</code></td>
<td>clusters, registries, shared queues</td>
<td>monthly</td>
</tr>
<tr>
<td><code>app-&#x3C;name></code></td>
<td>one service's infra, one per service</td>
<td>daily</td>
</tr>
</tbody>
</table>
<p>Cross-state references go through data sources or remote state reads, and the
dependency arrow only points down the table — apps read platform outputs,
never the reverse:</p>
<pre><code class="language-hcl">data "terraform_remote_state" "platform" {
  backend = "s3"

  config = {
    bucket = "acme-tfstate"
    key    = "platform/terraform.tfstate"
    region = "us-east-1"
  }
}

resource "aws_ecs_service" "api" {
  name    = "api"
  cluster = data.terraform_remote_state.platform.outputs.cluster_arn
}
</code></pre>
<h2>The migration nobody wants</h2>
<p>Splitting a monolithic state is <code>terraform state mv</code> drudgery, but it's
mechanical and it's reversible. The longer version of this post would cover
<code>moved</code> blocks and how to stage the split behind CI. The short version: do it
while the plan still finishes in single-digit minutes, because the cost only
compounds.</p>]]></content:encoded>
    </item>
    <item>
      <title>[SAMPLE] Idempotency is the only pipeline feature that matters</title>
      <link>https://aloutfi.com/writing/sample-idempotent-pipelines</link>
      <guid isPermaLink="true">https://aloutfi.com/writing/sample-idempotent-pipelines</guid>
      <pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate>
      <description>Retries, backfills, and 2am reruns all reduce to the same property. A sample post to verify typography — replace with real writing.</description>
      <content:encoded><![CDATA[<p><em>This is a sample post shipped with the site scaffold so the typography can be
checked against real-shaped content. Delete it before launch.</em></p>
<p>Every data pipeline eventually gets run twice. A worker dies mid-batch and the
orchestrator retries. Someone discovers a bug in June's numbers and backfills
the month. A deploy goes sideways and you rerun the day by hand at 2am. The
question is never <em>whether</em> a run will be repeated — it's whether repeating it
is safe.</p>
<h2>The property, stated plainly</h2>
<p>A pipeline step is idempotent when running it twice with the same inputs
produces the same state as running it once. Not <em>similar</em> state — the same
state. That rules out three habits that feel harmless in development:</p>
<ul>
<li>Appending to a table without a dedup key</li>
<li>Using wall-clock time as an input ("process everything since now minus one hour")</li>
<li>Writing partial output before the step has fully succeeded</li>
</ul>
<h2>What it looks like in practice</h2>
<p>The cheapest implementation is delete-then-insert scoped to the partition being
processed:</p>
<pre><code class="language-sql">BEGIN;

DELETE FROM fact_orders
 WHERE order_date = '2026-06-17';

INSERT INTO fact_orders
SELECT o.id,
       o.customer_id,
       o.total_cents,
       o.order_date
  FROM staging_orders o
 WHERE o.order_date = '2026-06-17';

COMMIT;
</code></pre>
<p>Run it once, you get June 17th. Run it five times, you still get June 17th.
The partition boundary is the contract: as long as every step owns a disjoint
slice of the output, retries are boring — which is the highest compliment you
can pay an operational property.</p>
<p>In an orchestrator, the same idea shows up as parameterizing every task by its
logical date rather than the current time:</p>
<pre><code class="language-python">@task
def load_orders(logical_date: date) -> None:
    """Load exactly one day; safe to rerun for any day, any number of times."""
    partition = read_staging("orders", day=logical_date)
    replace_partition("fact_orders", day=logical_date, rows=partition)
</code></pre>
<h2>Why this beats every other feature</h2>
<p>Monitoring, lineage, cataloging — all useful, all secondary. When a pipeline is
idempotent, incidents end with "rerun it." When it isn't, incidents end with a
forensic reconstruction of which rows were double-counted. One of these is a
five-minute fix. The other is a postmortem with an action-items section nobody
finishes.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
