<?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: Francisco Silvério</title>
    <description>The latest articles on DEV Community by Francisco Silvério (@franciscogsilverio).</description>
    <link>https://dev.to/franciscogsilverio</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3745895%2Fd0ecb00e-85c8-4037-af4d-00f855f05d3f.JPEG</url>
      <title>DEV Community: Francisco Silvério</title>
      <link>https://dev.to/franciscogsilverio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/franciscogsilverio"/>
    <language>en</language>
    <item>
      <title>Agentic Development as a Process, Not Throwaway Prompts</title>
      <dc:creator>Francisco Silvério</dc:creator>
      <pubDate>Sat, 17 Oct 2026 19:19:24 +0000</pubDate>
      <link>https://dev.to/franciscogsilverio/agentic-development-as-a-process-not-throwaway-prompts-3n8o</link>
      <guid>https://dev.to/franciscogsilverio/agentic-development-as-a-process-not-throwaway-prompts-3n8o</guid>
      <description>&lt;p&gt;Put five engineers on the same team, give them the same coding agent, and within a month you will have five different agentic workflows: a private prompt library, a rules file that never leaves a laptop, a clever skill nobody else knows exists. Each engineer is faster than before. The team is not, because the problem was never the tool. It was the process, or rather the absence of one.&lt;/p&gt;

&lt;p&gt;A team's agent setup (its rules, skills, connectors and quality gates) is shared engineering infrastructure, and it deserves to be designed, versioned and reviewed like any other part of the delivery system. Its purpose is not to remove the engineer, but to move the engineer's judgment to where it matters most. The examples use Claude Code, but every layer maps to open standards that Cursor, Codex and Windsurf support too.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. From Prompts to a Process
&lt;/h2&gt;

&lt;p&gt;A prompt is a one-time artifact. It lives in a single session, carries the assumptions of whoever wrote it, and disappears when the context window closes. Even an excellent prompt does not scale: nobody else can review it, test it, or improve it.&lt;/p&gt;

&lt;p&gt;A process is the opposite. It is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared:&lt;/strong&gt; every engineer runs the same workflow against the same definitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioned:&lt;/strong&gt; it lives in a repository, so every change has history and an author.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewed:&lt;/strong&gt; changes go through pull requests, like any other code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compounding:&lt;/strong&gt; a fix made once benefits every future session, for every engineer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We already accept this logic for CI/CD. Nobody expects each engineer to deploy with a personal script; the pipeline is a team asset with owners and a review process. Agentic workflows deserve the same treatment, and the data suggests they need it.&lt;/p&gt;

&lt;p&gt;Google's &lt;a href="https://dora.dev/research/ai" rel="noopener noreferrer"&gt;DORA research on AI-assisted software development&lt;/a&gt; describes AI as an &lt;strong&gt;amplifier&lt;/strong&gt;: it magnifies the strengths of high-performing organizations and the dysfunctions of struggling ones. The 2025 report also found that AI adoption now improves delivery throughput while still correlating negatively with delivery stability. Speed without a shared quality system turns into instability downstream.&lt;/p&gt;

&lt;p&gt;DORA's follow-up work, the &lt;a href="https://cloud.google.com/blog/products/ai-machine-learning/introducing-doras-inaugural-ai-capabilities-model" rel="noopener noreferrer"&gt;&lt;strong&gt;AI Capabilities Model&lt;/strong&gt;&lt;/a&gt;, identifies seven practices that determine whether AI helps or hurts an organization. Three of them read almost like a specification for the process in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A clear and communicated AI stance:&lt;/strong&gt; engineers know which tools and practices are expected. Ambiguity is a risk, not a freedom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-accessible internal data:&lt;/strong&gt; agents are connected to internal documentation and systems instead of working from prompts alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working in small batches:&lt;/strong&gt; a deliberate counterweight to agents' tendency to produce large changes that are hard to review and test.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr5kobxpj9daz35vyenja.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr5kobxpj9daz35vyenja.jpg" alt="Dora AI capabilities" width="799" height="393"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Reference Pipeline
&lt;/h2&gt;

&lt;p&gt;Setting up an agentic process starts with mapping everything an engineer usually does by hand. Fetching context for the agent or babysitting it through every step is not the engineer's job. Building the skills, tools and connectors that let it work on its own between the human checkpoints is. Here is what that workflow could look like for a typical development team:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    T["Ticket in the tracker"] --&amp;gt; I["Ingest"]
    I --&amp;gt; RD[("Requirements doc")]
    RD --&amp;gt; G1{{"Engineer validates requirements"}}
    G1 --&amp;gt; P["Plan"]
    P --&amp;gt; BL[("Versioned backlog of slices")]
    BL --&amp;gt; G2{{"Engineer approves the plan"}}
    G2 --&amp;gt; S["Spec for one slice"]
    S --&amp;gt; G3{{"Engineer approves the spec"}}
    G3 --&amp;gt; X["Implement"]
    X --&amp;gt; V["Verify: typecheck, tests, checks"]
    V -- fail --&amp;gt; X
    V -- pass --&amp;gt; AR["Adversarial review agents"]
    AR -- findings --&amp;gt; X
    AR -- clear --&amp;gt; PR["Pull request"]
    PR --&amp;gt; G4{{"Engineer reviews and merges"}}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Each stage produces an artifact that the next stage consumes, and each artifact is a file in the repository, not a message lost in a chat history. The hexagons are the human gates; we will come back to them in section 4.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ingest.&lt;/strong&gt; A connector reads the ticket and turns it into requirements: the problem, the acceptance criteria, the stakeholders, and where each requirement came from. A requirement with no source is one the agent invented.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan.&lt;/strong&gt; Requirements become small slices in a versioned backlog, each with its dependencies and its own definition of done. If the agent only ever implements one slice, it cannot produce a 3,000-line pull request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement.&lt;/strong&gt; For each slice, the agent writes a short spec (approach, files, edge cases, tests) and waits for the engineer's approval. Correcting a spec costs a minute; correcting finished code costs a review cycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify.&lt;/strong&gt; Typecheck, tests and checks run until they pass, and only then is a pull request opened. "Done" is defined by the pipeline, not claimed by the agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review.&lt;/strong&gt; Separate reviewer agents, each focused on one dimension (requirements, code quality, delivery plan), try to break the work before a human sees it. This is Anthropic's &lt;a href="https://www.anthropic.com/engineering/building-effective-agents" rel="noopener noreferrer"&gt;evaluator-optimizer&lt;/a&gt; pattern: the reviewers produce findings, and a human still decides what to act on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Traceability ties the stages together. Each slice records which acceptance criteria it covers, so every criterion can be traced to a test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S-03&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Paginated order history endpoint&lt;/span&gt;
&lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;S-01&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S-02&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;covers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;AC-2&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;AC-4&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;definition_of_done&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Returns orders for the authenticated customer only&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Cursor-based pagination with a maximum page size of &lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Integration tests cover AC-2 and AC-4, including the empty state&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Typecheck, lint and the full test suite pass&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ready&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That trace matters most for tests. A suite written by the same agent that wrote the code tends to confirm what the code does, not what the requirement says.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Toolkit, Layered by How Strongly It Holds
&lt;/h2&gt;

&lt;p&gt;The pipeline is executed by a toolkit with distinct layers. The most useful way to organize them is not by feature, but by how strongly each one constrains the agent.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart BT
    R["Rules: advisory&amp;lt;br/&amp;gt;conventions, boundaries, vocabulary"] --&amp;gt; SK["Skills: procedural&amp;lt;br/&amp;gt;ingest, plan, implement, review"]
    SK --&amp;gt; C["Connectors: grounded&amp;lt;br/&amp;gt;trackers, docs, CI, internal systems"]
    C --&amp;gt; E["Enforcement: deterministic&amp;lt;br/&amp;gt;hooks and CI gates that must pass"]&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rules (advisory).&lt;/strong&gt; Conventions, architectural boundaries and domain vocabulary, in &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt;. Anthropic's own docs describe them as advisory, so keep them short: every extra line dilutes the others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills (procedural).&lt;/strong&gt; Each pipeline stage is a skill the agent loads only when the task calls for it. This is where the team's process actually lives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connectors (grounded).&lt;/strong&gt; MCP servers for trackers (Jira, Linear), knowledge bases (Confluence, Notion), source control and CI (GitLab, GitHub), business systems (Salesforce) and other internal teams' services. Requirements then come from the source, not from someone's paraphrase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforcement (deterministic).&lt;/strong&gt; Hooks and CI gates for anything that must happen: formatting, blocking destructive commands, refusing "done" while tests fail. Guarantees go in hooks; preferences go in rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The toolkit lives in a repository and changes through pull requests, either as a committed configuration directory or, at scale, as plugins published through an internal marketplace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── AGENTS.md                 # shared rules, read by most agents
├── CLAUDE.md                 # symlink or one-line import of AGENTS.md
├── .mcp.json                 # team connectors
├── .claude/
│   ├── settings.json         # hooks and permissions, committed
│   ├── skills/
│   │   ├── ingest/
│   │   ├── plan/
│   │   ├── implement-slice/
│   │   └── review/
│   └── agents/               # reviewer subagents
└── docs/
    ├── requirements/         # output of ingest
    └── backlog/              # versioned slices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of this locks a team into one vendor. Rules map to AGENTS.md, skills to the Agent Skills standard, and connectors to MCP: open formats that most coding agents now read. The exception is enforcement. Agent-level hooks are tool-specific, so the gates that must hold regardless of who, or what, wrote the code belong in CI.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The Engineer at Each Gate
&lt;/h2&gt;

&lt;p&gt;Look at the pipeline again and count where a human appears. Not at every step: at four gates. That is the point. The process removes the engineer from the work that does not need judgment, so that judgment can be applied fully where it does.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbx6mlj5ie3695zb6zs7f.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbx6mlj5ie3695zb6zs7f.png" alt="The engineer at each gate of the process" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reviewer &lt;em&gt;role&lt;/em&gt; is increasingly automated; the review itself, the judgment of whether the software is right for its purpose, is not. And accountability does not delegate: the name on the approval belongs to a person.&lt;/p&gt;

&lt;p&gt;The research points the same way. A 2026 literature review on software engineering for agentic systems frames the engineer's enduring value around four competencies: intent articulation, systematic verification, multi-agent orchestration and accountable human judgment. None of them is typing code. All of them are engineering.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. You Won't Be Replaced. Unless You Stop Learning.
&lt;/h2&gt;

&lt;p&gt;Every shift in tooling triggers the same prediction. When deployments moved from manual runbooks to CI/CD pipelines, the engineers who stayed relevant were not the fastest at running scripts by hand; they were the ones who learned to build pipelines.&lt;/p&gt;

&lt;p&gt;When infrastructure moved from on-premise racks to cloud APIs and infrastructure as code, the people who struggled were not the ones whose tasks disappeared. They were the ones who refused to learn the new abstraction.&lt;/p&gt;

&lt;p&gt;Agentic development follows the same pattern, at a larger scale. The tasks are changing, and much of the typing goes away. What remains is understanding the problem, decomposing it, defining what correct means, and taking responsibility for the result. That was always the hard part of the job.&lt;/p&gt;

&lt;p&gt;Engineers are not being replaced by agents. Engineers who stop learning are being replaced by engineers who did not. That was true long before AI, and it will remain true after whatever comes next.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: Build the Process Once, Improve It Every Time
&lt;/h2&gt;

&lt;p&gt;A prompt helps one engineer, once. A process helps the whole team, every time, and it gets better with each mistake it absorbs. By treating rules, skills, connectors and quality gates as shared, versioned infrastructure, a team turns agent speed into delivery that meets requirements and holds the QA bar, instead of a faster way to produce pull requests nobody trusts.&lt;/p&gt;

&lt;p&gt;The agent writes the code. The team owns the process. The engineer owns the judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://research.google/pubs/dora-2025-state-of-ai-assisted-software-development-report/" rel="noopener noreferrer"&gt;DORA: State of AI-assisted Software Development 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/blog/products/ai-machine-learning/announcing-the-2025-dora-report" rel="noopener noreferrer"&gt;Google Cloud: Announcing the 2025 DORA Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/blog/products/ai-machine-learning/from-adoption-to-impact-putting-the-dora-ai-capabilities-model-to-work/" rel="noopener noreferrer"&gt;Google Cloud: Putting the DORA AI Capabilities Model to work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/engineering/building-effective-agents" rel="noopener noreferrer"&gt;Anthropic: Building effective agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/best-practices" rel="noopener noreferrer"&gt;Claude Code: Best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/plugins/overview" rel="noopener noreferrer"&gt;Claude Code: Plugins overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/news/donating-the-model-context-protocol-and-establishing-of-the-agentic-ai-foundation" rel="noopener noreferrer"&gt;Anthropic: Donating the Model Context Protocol and establishing the Agentic AI Foundation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://agents.md" rel="noopener noreferrer"&gt;AGENTS.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://agentskills.io" rel="noopener noreferrer"&gt;Agent Skills specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/github/spec-kit" rel="noopener noreferrer"&gt;GitHub Spec Kit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2604.10599" rel="noopener noreferrer"&gt;Rethinking Software Engineering for Agentic AI Systems (arXiv)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>softwareengineering</category>
      <category>agents</category>
    </item>
    <item>
      <title>Microservices with Bounded Contexts, Event-Driven Operations and Sagas: The Art of Orchestrating a Distributed System</title>
      <dc:creator>Francisco Silvério</dc:creator>
      <pubDate>Wed, 18 Feb 2026 13:17:11 +0000</pubDate>
      <link>https://dev.to/franciscogsilverio/microservices-beyond-the-theory-how-bounded-contexts-event-driven-operations-and-sagas-5go7</link>
      <guid>https://dev.to/franciscogsilverio/microservices-beyond-the-theory-how-bounded-contexts-event-driven-operations-and-sagas-5go7</guid>
      <description>&lt;p&gt;In a monolithic architecture, a transaction is a single, atomic database operation. It either happens in full, or it doesn't happen at all. It’s safe, but it can become a bottleneck when millions of users are trying to process transactions simultaneously. As we scale into distributed systems, we trade that localized safety for a "relay race" across multiple services. &lt;/p&gt;

&lt;p&gt;To prevent this relay race from ending in a catastrophe, we must move beyond the basic "microservices" buzzword and master the orchestration between services.&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%2Fdzqkeu5l0dvac3el3yro.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%2Fdzqkeu5l0dvac3el3yro.png" alt="Architecture Diagram" width="800" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The System: International Wire Transfer
&lt;/h2&gt;

&lt;p&gt;This architecture draft draws a microscope to a banking engine, specifically, the part responsible for wire transfers. Its mission is to move funds across borders while satisfying three distinct business requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Integrity:&lt;/strong&gt; Ensuring the math adds up and funds are accounted for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; Screening every transaction against global watchlists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Market Accuracy:&lt;/strong&gt; Locking in volatile currency exchange rates in real-time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of a single "Banking Service," we have three autonomous "islands" of logic: &lt;strong&gt;Ledger (Core)&lt;/strong&gt;, &lt;strong&gt;Fraud (Compliance)&lt;/strong&gt;, and &lt;strong&gt;FX (Currency)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Establishing the concepts
&lt;/h2&gt;

&lt;h4&gt;
  
  
  I. Bounded Context: The "Territory" of Data
&lt;/h4&gt;

&lt;p&gt;The term "Bounded Context" was originated by Eric Evans, who defines it as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A description of a boundary (typically a subsystem, or the work of a particular team) within which a particular model is defined and applicable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Looking at the diagram, you’ll notice that the &lt;strong&gt;Entity: Account&lt;/strong&gt; exists in all three services, but the &lt;strong&gt;Account Model&lt;/strong&gt; is different in each one. This is the &lt;strong&gt;Bounded Context&lt;/strong&gt; in action.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In the Ledger Context:&lt;/strong&gt; An account is a &lt;em&gt;Balance Container&lt;/em&gt;. It only cares about &lt;code&gt;Available Balance&lt;/code&gt; and &lt;code&gt;Currency&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In the Fraud Context:&lt;/strong&gt; An account is a &lt;em&gt;Legal Risk Profile&lt;/em&gt;. It cares about the &lt;code&gt;Owner Legal Name&lt;/code&gt; and &lt;code&gt;Watchlist Flags&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In the FX Context:&lt;/strong&gt; An account is a &lt;em&gt;Regional Wallet&lt;/em&gt;. It cares about the &lt;code&gt;Home ISO&lt;/code&gt; and the &lt;code&gt;Local Market Rate&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By isolating these models, we ensure that a change in the Compliance department's risk-rating logic never accidentally breaks the code responsible for calculating account balances. Each service owns its own "version of the truth."&lt;/p&gt;

&lt;h4&gt;
  
  
  II. Event-Driven Operations: The "Nervous System"
&lt;/h4&gt;

&lt;p&gt;In the provided architecture, notice that the services never call each other's APIs directly. They communicate via a &lt;strong&gt;Message Bus&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a request arrives, the Ledger doesn't "tell" Fraud to start a check; it simply announces a fact: &lt;code&gt;TransferRequested&lt;/code&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling:&lt;/strong&gt; The Ledger doesn't know (or care) if the Fraud service is currently online. It drops the message on the Bus and continues its work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomy:&lt;/strong&gt; The Fraud and FX services are "Subscribers." They react to the message when they have the resources to do so. This prevents a slow-down in one service from cascading into a total system failure.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. The Saga: Orchestrating the Distributed "Undo"
&lt;/h2&gt;

&lt;p&gt;Now that we have established what the system does and why it is organized this way, one question might pop up: If these services are independent islands, who ensures the process actually finishes? &lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;Saga Pattern&lt;/strong&gt; enters.&lt;/p&gt;

&lt;p&gt;In a distributed system, there is no "Global Rollback." If the money is locked in the Ledger (Step 2) and the Fraud service emits a &lt;code&gt;SecurityCleared&lt;/code&gt; message, but the FX system finds a problem, there is no way to automatically revert all the other database records. We must perform a &lt;strong&gt;Compensating Transaction&lt;/strong&gt;.&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%2F9qpd98n0ydeg8ghr874j.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%2F9qpd98n0ydeg8ghr874j.png" alt="Saga Pattern Flow" width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How the Saga Orchestrates the System:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Happy Path:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ledger&lt;/strong&gt; locks the funds emits &lt;code&gt;TransferRequested&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fraud&lt;/strong&gt; clears the user emits &lt;code&gt;SecurityCleared&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FX&lt;/strong&gt; locks the conversion rate emits &lt;code&gt;RateLocked&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; The transfer is finalized.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Failure Path (The "Undo" Button):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ledger&lt;/strong&gt; locks $10,000 and emits &lt;code&gt;TransferRequested&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fraud Service&lt;/strong&gt; checks pass and it emits a &lt;code&gt;SecurityCleared&lt;/code&gt; message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FX Service&lt;/strong&gt; fails to process the transaction and emits &lt;code&gt;TransactionNotAllowed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Compensation:&lt;/strong&gt; The Ledger and Fraud services are "listening" for failure events. Upon hearing &lt;code&gt;TransactionNotAllowed&lt;/code&gt;, they trigger their own internal "Undo" logic to unlock the $10,000 and notify the user.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Choreography vs. Orchestration
&lt;/h3&gt;

&lt;p&gt;Our system uses &lt;strong&gt;Choreography&lt;/strong&gt;. There is no central "Boss" or "Orchestrator" service. The Ledger works as an entry point for the operation, but it is not responsible for managing the state of the entire flow. &lt;/p&gt;

&lt;p&gt;If we required more granular control over the operation, we could implement &lt;strong&gt;Orchestration&lt;/strong&gt;, where a dedicated service directs the transaction and has the final word to approve or deny it. For many decentralized systems, however, the Choreography shown here offers the best balance of scale and simplicity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: Resilience by Design
&lt;/h2&gt;

&lt;p&gt;By combining &lt;strong&gt;Bounded Contexts&lt;/strong&gt; (to isolate logic), &lt;strong&gt;Event-Driven Operations&lt;/strong&gt; (to decouple communication), and &lt;strong&gt;Sagas&lt;/strong&gt; (to handle failures), we build a system that is far more resilient and performant than any monolith. We accept "Eventual Consistency" in exchange for a system that can scale globally and fail gracefully.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>microservices</category>
      <category>api</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Automating React App deployments to AWS with GitHub Actions and OIDC</title>
      <dc:creator>Francisco Silvério</dc:creator>
      <pubDate>Sun, 01 Feb 2026 21:26:37 +0000</pubDate>
      <link>https://dev.to/franciscogsilverio/automating-react-app-deployments-to-aws-with-github-actions-and-oidc-3487</link>
      <guid>https://dev.to/franciscogsilverio/automating-react-app-deployments-to-aws-with-github-actions-and-oidc-3487</guid>
      <description>&lt;p&gt;After deploying my &lt;a href="https://portfolio.franciscogsilverio.com/" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; to AWS, I realized the need to automate the deployment process to streamline the delivery of new features. For that, you can’t go wrong with a CI/CD pipeline powered by your Git provider — in this case, GitHub and GitHub Actions.&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk through the architecture and CI/CD pipeline I use to deploy a React application to S3 + CloudFront, authenticated via GitHub Actions OIDC (no long-lived AWS credentials).&lt;/p&gt;

&lt;p&gt;This setup is suitable for real-world projects and follows AWS and GitHub best practices.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Before diving into CI/CD, let’s clarify the infrastructure that was &lt;strong&gt;already in place&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Initial AWS Setup (Pre-requisites)
&lt;/h2&gt;

&lt;p&gt;The following resources were already created before the pipeline was written.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 - S3 bucket&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static website hosting enabled&lt;/li&gt;
&lt;li&gt;Public access configured appropriately&lt;/li&gt;
&lt;li&gt;Hosts the built React assets (index.html, /assets/*, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2 - CloudFront distribution&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Origin pointing to the S3 website endpoint&lt;/li&gt;
&lt;li&gt;Default root object: index.html&lt;/li&gt;
&lt;li&gt;Caching enabled&lt;/li&gt;
&lt;li&gt;Custom domain configured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3 - Name.com&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom domain pointing to the CloudFront distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this setup is complete, the website can already be accessed via the custom domain.&lt;br&gt;
The CI/CD pipeline’s job is to automate updates safely and consistently.&lt;/p&gt;


&lt;h2&gt;
  
  
  AWS: Configuring OIDC Authentication
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1 - Create an OIDC Identity Provider&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In AWS IAM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provider URL:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://token.actions.githubusercontent.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Audience:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sts.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This allows AWS to trust GitHub as an identity provider.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;2 - Create an IAM Role for GitHub Action&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This role will be assumed by the GitHub Actions runner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:oidc-provider/token.actions.githubusercontent.com"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringLike"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"repo:&amp;lt;ORG&amp;gt;/&amp;lt;REPO&amp;gt;:*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3 - Attach Required IAM Permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The role needs permissions to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sync files to S3&lt;/li&gt;
&lt;li&gt;Create CloudFront invalidations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example policy (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&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="w"&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="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::&amp;lt;BUCKET_NAME&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::&amp;lt;BUCKET_NAME&amp;gt;/*"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cloudfront:CreateInvalidation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  GitHub Actions Pipeline Overview
&lt;/h2&gt;

&lt;p&gt;The pipeline is split into three jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;install – install dependencies&lt;/li&gt;
&lt;li&gt;build – build the React app and upload artifacts&lt;/li&gt;
&lt;li&gt;deploy – authenticate to AWS, deploy to S3, invalidate CloudFront&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This separation improves clarity, caching, and debuggability.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Job 1: Installing Dependencies&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;install:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - name: Install dependencies
      uses: actions/setup-node@v4
      with:
        node-version: "22.x"
        cache: 'npm'
    - run: npm ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this job does&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks out the repository&lt;/li&gt;
&lt;li&gt;Sets up Node.js 22&lt;/li&gt;
&lt;li&gt;Uses npm ci for deterministic installs&lt;/li&gt;
&lt;li&gt;Enables dependency caching for faster builds&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This job ensures the dependency tree is valid before moving forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job 2: Building the React Application&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  runs-on: ubuntu-latest
  needs: install
  steps:
    - uses: actions/checkout@v4
    - name: Use Node.js 22
      uses: actions/setup-node@v4
      with:
        node-version: "22.x"
        cache: 'npm'
    - run: npm ci
    - run: npm run build
    - name: Upload dist
      uses: actions/upload-artifact@v4
      with:
        name: dist
        path: ./dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this job does&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rebuilds the application in a clean environment&lt;/li&gt;
&lt;li&gt;Produces a static dist/ folder&lt;/li&gt;
&lt;li&gt;Uploads the build output as a pipeline artifact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Artifacts allow the deploy job to be fully decoupled from the build process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job 3: Deploying to AWS (S3 + CloudFront)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: aws
    permissions:
      id-token: write
      contents: read

    steps:
      - name: Download dist artifact
        uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ARN }}
          aws-region: ${{ vars.AWS_REGION }}
          role-session-name: ${{ vars.ROLE_SESSION_NAME}}

      - name: Deploy to S3
        run: |
          aws s3 sync dist s3://${{ vars.S3_BUCKET_NAME }} \
            --delete \
            --exact-timestamps

      - name: Invalidate CloudFront
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }} \
            --paths "/*"

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this job does&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloads the build artifact&lt;/li&gt;
&lt;li&gt;Authenticates to AWS&lt;/li&gt;
&lt;li&gt;Uploads the static files to the bucket&lt;/li&gt;
&lt;li&gt;Invalidate CloudFront's cache&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Invalidating /* ensures CloudFront fetches the new version immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Result
&lt;/h2&gt;

&lt;p&gt;After every push to main:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app is built&lt;/li&gt;
&lt;li&gt;Assets are uploaded to S3&lt;/li&gt;
&lt;li&gt;CloudFront cache is invalidated&lt;/li&gt;
&lt;li&gt;The custom domain serves the latest version reliably&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All without storing a single AWS secret in GitHub.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect" rel="noopener noreferrer"&gt;- GitHub Actions OIDC&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html" rel="noopener noreferrer"&gt;- AWS IAM OIDC&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html" rel="noopener noreferrer"&gt;- CloudFront invalidations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aws-actions/configure-aws-credentials" rel="noopener noreferrer"&gt;- aws-actions/configure-aws-credentials&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>githubactions</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
