<?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: StackOps AI</title>
    <description>The latest articles on DEV Community by StackOps AI (@stackops_ai_b57b69edea711).</description>
    <link>https://dev.to/stackops_ai_b57b69edea711</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%2F3682085%2F342291b9-425a-4697-8a6e-6bce98bb0a48.png</url>
      <title>DEV Community: StackOps AI</title>
      <link>https://dev.to/stackops_ai_b57b69edea711</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stackops_ai_b57b69edea711"/>
    <language>en</language>
    <item>
      <title>How We Migrated an AWS Amplify GraphQL Backend to CDK (Without a Rewrite)</title>
      <dc:creator>StackOps AI</dc:creator>
      <pubDate>Tue, 30 Dec 2025 10:29:46 +0000</pubDate>
      <link>https://dev.to/stackops_ai_b57b69edea711/how-we-migrated-an-aws-amplify-graphql-backend-to-cdk-without-a-rewrite-2888</link>
      <guid>https://dev.to/stackops_ai_b57b69edea711/how-we-migrated-an-aws-amplify-graphql-backend-to-cdk-without-a-rewrite-2888</guid>
      <description>&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%2Fb9oj4gr98zju6k3j59ew.jpeg" 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%2Fb9oj4gr98zju6k3j59ew.jpeg" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;br&gt;
AWS Amplify is excellent for getting started with a GraphQL backend.&lt;br&gt;
It removes friction, scaffolds infrastructure quickly, and lets small teams move fast.&lt;br&gt;
But as systems grow, many teams reach a point where Amplify becomes a constraint rather than an accelerator.&lt;br&gt;
In this post, I'll walk you through how we migrated an existing AWS Amplify AppSync backend to AWS CDK without rewriting business logic, why we did it, and what we learned along the way.&lt;br&gt;
This is not an anti-Amplify post. It's about understanding where Amplify fits - and where it stops scaling.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Amplify Works Well
&lt;/h2&gt;

&lt;p&gt;Amplify is a great choice when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're early in a project&lt;/li&gt;
&lt;li&gt;You want fast schema-driven development&lt;/li&gt;
&lt;li&gt;You're happy with Amplify-managed CloudFormation&lt;/li&gt;
&lt;li&gt;You don't need fine-grained IAM or custom pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We used Amplify exactly this way at the start.&lt;br&gt;
Using directives like @model, &lt;a class="mentioned-user" href="https://dev.to/auth"&gt;@auth&lt;/a&gt;, and connections, Amplify generated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AppSync GraphQL API&lt;/li&gt;
&lt;li&gt;DynamoDB tables&lt;/li&gt;
&lt;li&gt;VTL resolvers&lt;/li&gt;
&lt;li&gt;IAM roles&lt;/li&gt;
&lt;li&gt;Lambda data sources where needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a long time, this worked well.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problems We Eventually Hit
&lt;/h2&gt;

&lt;p&gt;As the backend grew, a few issues became increasingly hard to ignore.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Limited control and visibility
&lt;/h3&gt;

&lt;p&gt;Amplify abstracts away a lot of infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM permissions are auto-generated&lt;/li&gt;
&lt;li&gt;CloudFormation stacks are hidden&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resource naming is opaque&lt;br&gt;
This makes:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code reviews harder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security reviews painful&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Debugging deployments difficult&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Difficult multi-environment and platform integration
&lt;/h3&gt;

&lt;p&gt;We wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit dev / staging / prod environments&lt;/li&gt;
&lt;li&gt;Integration with an existing CDK-based platform&lt;/li&gt;
&lt;li&gt;Predictable diffs and rollbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Amplify’s CLI-driven workflow didn’t fit well with this.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A hard CloudFormation scaling limit
&lt;/h3&gt;

&lt;p&gt;This was the real forcing function. AWS CloudFormation enforces a hard limit of 500 resources per stack.&lt;/p&gt;

&lt;p&gt;Amplify deploys most GraphQL backends into one or a very small number of CloudFormation stacks. As schemas grow, the number of generated resources grows quickly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resolvers&lt;/li&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;IAM roles and policies&lt;/li&gt;
&lt;li&gt;DynamoDB tables and GSIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you approach that 500-resource limit per stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployments become fragile&lt;/li&gt;
&lt;li&gt;Adding new models or resolvers can fail&lt;/li&gt;
&lt;li&gt;There is no supported way in Amplify to split or refactor the generated stacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, backend evolution effectively stalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Didn’t Rewrite Everything
&lt;/h2&gt;

&lt;p&gt;By the time we hit these limits, we already had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A large GraphQL schema&lt;/li&gt;
&lt;li&gt;Custom VTL resolvers&lt;/li&gt;
&lt;li&gt;Lambda-based business logic&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Production data in DynamoDB&lt;br&gt;
A full rewrite would have been:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Risky&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time-consuming&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unnecessary&lt;br&gt;
Instead, we asked a different question:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;What if Amplify was only used to generate the initial artifacts — and not to own the backend forever?&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Use Amplify Once — as a Scaffolding Tool
&lt;/h3&gt;

&lt;p&gt;The core idea is that Amplify can remain your compiler, while CDK becomes your deployment engine. We treated Amplify as a generator, not a long-term platform.&lt;/p&gt;

&lt;p&gt;Using Amplify, we generated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;schema.graphql&lt;/li&gt;
&lt;li&gt;.vtl resolver templates&lt;/li&gt;
&lt;li&gt;Build artifacts:&lt;/li&gt;
&lt;li&gt;— build/cloudformation-template.json&lt;/li&gt;
&lt;li&gt;— build/stacks/*.json&lt;/li&gt;
&lt;li&gt;— build/resolvers/*.vtl&lt;/li&gt;
&lt;li&gt;Auth logic embedded in resolvers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage, Amplify did its job well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Extract the Durable Assets
&lt;/h3&gt;

&lt;p&gt;From the Amplify backend output, we extracted only what was durable and valuable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GraphQL schema&lt;/li&gt;
&lt;li&gt;Resolver templates&lt;/li&gt;
&lt;li&gt;Table definitions&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auth rules and logic&lt;br&gt;
We explicitly did not keep:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Amplify CLI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Amplify Console&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auto-generated CloudFormation stacks&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are implementation details — not architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Rebuild Explicitly in AWS CDK
&lt;/h3&gt;

&lt;p&gt;Each Amplify-generated resource was reimplemented explicitly in AWS CDK. CDK takes over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AppSync API + data sources&lt;/li&gt;
&lt;li&gt;FunctionConfigurations&lt;/li&gt;
&lt;li&gt;Pipeline resolvers&lt;/li&gt;
&lt;li&gt;IAM roles/policies&lt;/li&gt;
&lt;li&gt;DynamoDB tables (optional)&lt;/li&gt;
&lt;li&gt;Lambda data sources (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of deploying Amplify stacks directly, we extract the intent into YAML and redeploy from there.This removed Amplify “magic” and made behaviour predictable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Split the Backend into Multiple CDK Stacks
&lt;/h3&gt;

&lt;p&gt;Unlike Amplify’s monolithic stacks, CDK allowed us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Split AppSync, DynamoDB, Lambda, and IAM into separate stacks&lt;/li&gt;
&lt;li&gt;Control resource boundaries&lt;/li&gt;
&lt;li&gt;Avoid CloudFormation’s 500-resource limit entirely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This single change removed a major long-term scalability risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Config-Driven, Environment-Aware Design
&lt;/h3&gt;

&lt;p&gt;We replaced CLI-driven configuration with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YAML-based config files&lt;/li&gt;
&lt;li&gt;Environment-specific definitions (dev, staging, prod)&lt;/li&gt;
&lt;li&gt;Deterministic naming&lt;/li&gt;
&lt;li&gt;Reviewable diffs via cdk diff
Deployments now look like:
&lt;code&gt;cdk diff -c env=staging
cdk deploy -c env=staging&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CDK became the only source of truth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Remove Amplify Completely
&lt;/h3&gt;

&lt;p&gt;Once parity was verified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amplify project was removed&lt;/li&gt;
&lt;li&gt;Amplify Console disconnected&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No more amplify push&lt;br&gt;
From that point on:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Infrastructure changes are code-reviewed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deployments are predictable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scaling is no longer capped by stack limits&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Amplify is excellent for scaffolding&lt;/li&gt;
&lt;li&gt;It is not designed for large, long-lived backends&lt;/li&gt;
&lt;li&gt;The CloudFormation 500-resource limit is a real constraint&lt;/li&gt;
&lt;li&gt;Migration is safer than rewriting&lt;/li&gt;
&lt;li&gt;Explicit CDK ownership pays off quickly at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Want the Full Setup?
&lt;/h2&gt;

&lt;p&gt;We packaged this approach into a reusable bundle that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A production-grade CDK AppSync backend&lt;/li&gt;
&lt;li&gt;Config-driven resolver and Lambda wiring&lt;/li&gt;
&lt;li&gt;Migration checklist and hard-earned lessons&lt;/li&gt;
&lt;li&gt;Real-world examples (not toy demos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Gumroad: &lt;a href="https://stackopsai.gumroad.com/l/eizwk" rel="noopener noreferrer"&gt;https://stackopsai.gumroad.com/l/eizwk&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This migration wasn’t about rejecting Amplify. It was about using the right tool at the right stage.&lt;/p&gt;

&lt;p&gt;Amplify helped us move fast early. CDK helped us move safely at scale.&lt;/p&gt;

&lt;p&gt;If you’re approaching the same limits, there is a clean exit — without a rewrite.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>aws</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
