<?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: Aparna Krishnamoorthy</title>
    <description>The latest articles on DEV Community by Aparna Krishnamoorthy (@aparkris).</description>
    <link>https://dev.to/aparkris</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%2F3927622%2F9eb7d712-e035-4ef1-812f-a1f71401f76b.png</url>
      <title>DEV Community: Aparna Krishnamoorthy</title>
      <link>https://dev.to/aparkris</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aparkris"/>
    <language>en</language>
    <item>
      <title>Gradual rollout with AWS AppConfig: change configuration slowly and watch the metrics</title>
      <dc:creator>Aparna Krishnamoorthy</dc:creator>
      <pubDate>Thu, 15 Oct 2026 13:17:41 +0000</pubDate>
      <link>https://dev.to/aparkris/gradual-rollout-with-aws-appconfig-change-configuration-slowly-and-watch-the-metrics-4j15</link>
      <guid>https://dev.to/aparkris/gradual-rollout-with-aws-appconfig-change-configuration-slowly-and-watch-the-metrics-4j15</guid>
      <description>&lt;p&gt;A configuration change looks harmless. You flip a feature flag, adjust a timeout, raise a rate limit, and push it out. There's no build, no deploy pipeline, no code review gate. That's the appeal of dynamic configuration: you change behavior in seconds without shipping code.&lt;/p&gt;

&lt;p&gt;It's also the risk. When a configuration change deploys to 100% of your fleet at once, a bad value reaches every user before you can react. The blast radius is the whole system. The time to notice is however long it takes an alarm to fire, a customer to complain, or a dashboard to turn red. By then it's everywhere.&lt;/p&gt;

&lt;p&gt;AWS AppConfig is built around a different assumption: change configuration slowly and watch the metrics while it rolls out. You can't start an AppConfig deployment without choosing a &lt;strong&gt;deployment strategy&lt;/strong&gt;. The strategy says how fast the change spreads and how long AppConfig keeps watching afterward. This post explains how deployment strategies work, what AppConfig needs from you to make the watching real, and why "slowly, while watching" is the single most important habit for safe configuration changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with all-at-once
&lt;/h2&gt;

&lt;p&gt;Imagine you lower a downstream call timeout, managed in AppConfig, from 3,000 ms to 1,500 ms so slow requests stop tying up threads. The value is perfectly valid. A schema validator would wave it through. What you don't know is that the downstream's tail latency sits around 1,800 ms at peak. Deploy it all at once and every caller picks up the new value within a minute or so. Requests that used to succeed slowly now time out. Callers retry, the retries pile on load, and the failure is fleet-wide before your on-call has finished reading the first alert.&lt;/p&gt;

&lt;p&gt;Now imagine you roll the same change out to 10% of your fleet first, with Amazon CloudWatch alarms on the environment so AppConfig can watch your error rate. The bad value still ships, but it reaches a tenth of your callers. An alarm goes off, and AppConfig rolls the deployment back. The other 90% never receive it, and the 10% that did receive it get the old value back on their next poll. Same mistake, a fraction of the damage.&lt;/p&gt;

&lt;p&gt;That's the whole idea. You can't stop every bad value from being created. A value that's valid but still wrong, like this one, slips past validators and only reveals itself in production. What a gradual rollout gives you is &lt;em&gt;time and containment&lt;/em&gt;: the change reaches a small slice of your fleet long enough for your metrics to say whether it's healthy, before it reaches everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick orientation
&lt;/h2&gt;

&lt;p&gt;Before we get into the mechanics, a few terms are worth pinning down. In AppConfig, an &lt;strong&gt;application&lt;/strong&gt; is a logical grouping for one app's configuration. Within it, an &lt;strong&gt;environment&lt;/strong&gt; is a place you deploy to, such as &lt;code&gt;production&lt;/code&gt; or &lt;code&gt;staging&lt;/code&gt;. A &lt;strong&gt;configuration profile&lt;/strong&gt; is one piece of configuration data you deploy: a set of feature flags, or a free-form JSON, YAML, or text document. A &lt;strong&gt;deployment&lt;/strong&gt; takes a version of one profile and rolls it out to one environment.&lt;/p&gt;

&lt;p&gt;That last step, rolling a profile's new version out to an environment, is where a deployment strategy comes in. It governs the rollout itself, and it's what the rest of this post is about.&lt;/p&gt;

&lt;p&gt;Rollout progress is measured in &lt;strong&gt;targets&lt;/strong&gt;, and that word earns a precise definition because it does a lot of work below. When AppConfig says a deployment has reached 10% of targets, it's counting the callers that poll for the configuration, not machines. Each caller holds a configuration session, and AppConfig decides session by session whether it's in the current slice. One AppConfig Agent per host makes a target effectively a host. Several sessions per host, or Lambda execution environments, make the mapping looser. You can also use the AppConfig Agent to roll out by user or segment instead, so a given user sees one consistent version throughout the rollout no matter which host serves them. See &lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-agent-how-to-use.html#appconfig-entity-based-gradual-deployments" rel="noopener noreferrer"&gt;entity-based gradual deployments&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a deployment strategy is
&lt;/h2&gt;

&lt;p&gt;Every deployment names a &lt;strong&gt;deployment strategy&lt;/strong&gt;: a reusable definition of &lt;em&gt;how fast&lt;/em&gt; the change rolls out and &lt;em&gt;how long&lt;/em&gt; AppConfig watches before calling it done. A strategy isn't tied to an application, so you define it once and select it on each deployment. "How we roll out config" becomes something you decide up front and pick from a list, not something you improvise every time.&lt;/p&gt;

&lt;p&gt;A deployment strategy is built from four settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Growth type&lt;/strong&gt;: the shape of the rollout curve, linear or exponential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Growth factor&lt;/strong&gt;: how big each step is, as a percentage of targets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment time&lt;/strong&gt;: the total window over which those steps are spread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bake time&lt;/strong&gt;: how long AppConfig keeps watching your CloudWatch alarms after the configuration reaches 100% of targets, before it considers the deployment complete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get these four right and you've described exactly how cautiously, or how quickly, a change reaches your fleet. Let's take them one at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Growth type: the shape of the curve
&lt;/h3&gt;

&lt;p&gt;Growth type controls how the percentage of targets grows at each step. AppConfig supports two, linear and exponential. The console calls this setting &lt;strong&gt;deployment type&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linear&lt;/strong&gt; grows by the same increment every step. A rollout with a 20% growth factor moves 0% → 20% → 40% → 60% → 80% → 100%, in even steps across the deployment window. Every interval exposes the same additional slice of your fleet, which makes it predictable and easy to reason about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exponential&lt;/strong&gt; starts small and accelerates. It follows the formula &lt;code&gt;G * (2^N)&lt;/code&gt;, where &lt;code&gt;G&lt;/code&gt; is the growth factor and &lt;code&gt;N&lt;/code&gt; is the step number, counting from zero. With a growth factor of 2, the rollout reaches 2% of targets, then 4%, then 8%, then 16%, and so on until it hits 100%. This keeps early exposure very small at the riskiest moment (when you know least about the change) and widens quickly once the first steps clear. It's the classic canary shape: tiptoe out, then stride once nothing has broken. The trade-off is at the far end, where the steps are big jumps. With a 10% growth factor they run 40% → 80% → 100%, so a problem that only surfaces at majority exposure arrives in one leap rather than several.&lt;/p&gt;

&lt;p&gt;As a rule of thumb: exponential for maximum caution at the start, linear for steady, evenly paced exposure all the way through.&lt;/p&gt;

&lt;h3&gt;
  
  
  Growth factor: the size of each step
&lt;/h3&gt;

&lt;p&gt;The growth factor is the percentage of targets that receive the configuration during each step. It can be anything from 1 to 100. The console calls it &lt;strong&gt;step percentage&lt;/strong&gt;; the API, CLI, and SDKs call it &lt;strong&gt;growth factor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For linear growth, the factor is the size of every step. For exponential growth, it's the &lt;code&gt;G&lt;/code&gt; in &lt;code&gt;G * (2^N)&lt;/code&gt;: the size of the first step, which then doubles each step until the configuration reaches all targets. The final step is capped at 100%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment time: how long the whole thing takes
&lt;/h3&gt;

&lt;p&gt;Deployment time is the total window over which AppConfig spreads the steps. Note what it is &lt;em&gt;not&lt;/em&gt;: a timeout. It's the window during which the deployment is processed in intervals. A 30-minute deployment with 20% linear growth advances one 20% step every six minutes until it reaches 100%. Exponential works the same way. A 10% growth factor takes five steps to reach 100% (10 → 20 → 40 → 80 → 100%), so a 20-minute exponential deployment advances roughly every four minutes.&lt;/p&gt;

&lt;p&gt;One detail is easy to miss: AppConfig doesn't push configuration to callers. They poll for it. "Reached 20% of targets" means 20% of sessions are now &lt;em&gt;eligible&lt;/em&gt;, and each picks up the new version on its next poll. So real exposure lags the deployment's percentage by up to one poll interval. The default is 45 seconds with the AppConfig Agent. The same lag applies in reverse. After a rollback, callers that got the bad value revert on their next poll.&lt;/p&gt;

&lt;p&gt;Deployment time is your main lever for pacing. A longer window gives your metrics more time to reveal a problem at each stage. A shorter one ships faster but leaves less room to notice trouble between steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bake time: watching after full deployment
&lt;/h3&gt;

&lt;p&gt;Reaching 100% of targets isn't the end of the deployment. Bake time is how long AppConfig keeps monitoring your CloudWatch alarms &lt;em&gt;after&lt;/em&gt; the configuration reaches every target, before marking the deployment complete.&lt;/p&gt;

&lt;p&gt;It matters because some problems don't surface during the rollout itself: a slow memory leak, a cache that only fills after a few minutes, a scheduled job that fires on the hour. Bake time keeps AppConfig watching for a defined window afterward. If an alarm goes off during it, AppConfig rolls the deployment back exactly as it would mid-rollout. The next section covers what AppConfig needs from you for that to happen.&lt;/p&gt;

&lt;p&gt;Once bake time ends, the deployment is complete and automatic rollback is off. If you discover a problem after that, reverting is a deliberate act rather than an automatic one. See &lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-deploying-reverting.html" rel="noopener noreferrer"&gt;Reverting a configuration&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alarms: the setup that makes rollback automatic
&lt;/h2&gt;

&lt;p&gt;Everything above about AppConfig "watching your alarms" depends on a setup step that's easy to skip. On its own, AppConfig knows nothing about your application's health. You attach one or more CloudWatch alarms to the &lt;strong&gt;environment&lt;/strong&gt;, plus an IAM role AppConfig can assume to read them. With that in place, AppConfig checks those alarms throughout the rollout and the bake time. It rolls back automatically if any goes off, whether at 10% exposure or in the last minute of the bake.&lt;/p&gt;

&lt;p&gt;Without it, a deployment strategy still slows the rollout down, but nothing stops it for you. You watch the dashboard yourself, and your safety net is stopping an in-progress deployment by hand, which rolls it back. That's a reasonable way to run a small system, as long as you know that's the mode you're in.&lt;/p&gt;

&lt;p&gt;Three details are worth knowing before you rely on automatic rollback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It triggers on &lt;code&gt;INSUFFICIENT_DATA&lt;/code&gt;, not only on &lt;code&gt;ALARM&lt;/code&gt;.&lt;/strong&gt; That's the conservative choice, but it has a catch. An alarm whose metric goes quiet during a rollout, such as one on a low-traffic endpoint or one that only reports when an error occurs, can roll back a perfectly healthy deployment. The fix is to match the alarm's missing-data treatment to the metric: a metric that reports continuously must treat missing data as breaching, while one that only emits on errors must treat it as not breaching. (The default treatment is &lt;code&gt;missing&lt;/code&gt;, which is what sends a quiet alarm to &lt;code&gt;INSUFFICIENT_DATA&lt;/code&gt;.) Also give a new alarm time to evaluate before you deploy, since a freshly created alarm sits in &lt;code&gt;INSUFFICIENT_DATA&lt;/code&gt; until its first evaluation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It ignores alarms whose actions are disabled.&lt;/strong&gt; If someone silenced an alarm for maintenance, it won't roll back your deployment either.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your metrics don't have to live in CloudWatch.&lt;/strong&gt; If you monitor with a third-party tool, an AppConfig extension can check it at each deployment tick and trigger the same rollback. AWS publishes examples for Datadog and New Relic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The wiring is covered in &lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/monitoring-deployments.html" rel="noopener noreferrer"&gt;Monitoring deployments for automatic rollback&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Predefined strategies vs. rolling your own
&lt;/h2&gt;

&lt;p&gt;You don't have to design a strategy from scratch. AppConfig ships four predefined strategies that cover the common cases:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Rollout&lt;/th&gt;
&lt;th&gt;Bake time&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AppConfig.Canary10Percent20Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exponential, 10% growth factor, 20 minutes (10 → 20 → 40 → 80 → 100%)&lt;/td&gt;
&lt;td&gt;10 minutes&lt;/td&gt;
&lt;td&gt;Production; caution up front, AWS-recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AppConfig.Linear20PercentEvery6Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Linear, 20% every 6 minutes, 30 minutes (20 → 40 → 60 → 80 → 100%)&lt;/td&gt;
&lt;td&gt;30 minutes&lt;/td&gt;
&lt;td&gt;Production; steady pace and a long bake, AWS-recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AppConfig.AllAtOnce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;100% immediately&lt;/td&gt;
&lt;td&gt;10 minutes&lt;/td&gt;
&lt;td&gt;Low-risk changes and emergencies, when you accept full exposure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AppConfig.Linear50PercentEvery30Seconds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Linear, 50% every 30 seconds, 1 minute (50 → 100%)&lt;/td&gt;
&lt;td&gt;1 minute&lt;/td&gt;
&lt;td&gt;Testing and demos only, not production&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things stand out in that table. First, even &lt;code&gt;AllAtOnce&lt;/code&gt; isn't unmonitored. The change ships to everyone immediately, but AppConfig still bakes for 10 minutes. So with alarms on the environment you get automatic rollback even when you skip the gradual part. What you lose is containment: by the time an alarm fires, every caller already has the value. Second, &lt;code&gt;Linear50PercentEvery30Seconds&lt;/code&gt; is for testing and demos. Its one-minute duration and bake time are deliberately too short to catch much.&lt;/p&gt;

&lt;p&gt;For most production workloads, one of the two AWS-recommended strategies is the right starting point: &lt;code&gt;Canary10Percent20Minutes&lt;/code&gt; or &lt;code&gt;Linear20PercentEvery6Minutes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The predefined strategies won't always fit. Maybe you want a very long bake time for a change you're especially nervous about, or a smaller first step for a large fleet. Then you create your own. Here's a custom strategy that rolls out exponentially at a 10% growth factor over an hour, then bakes for a full hour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws appconfig create-deployment-strategy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"Cautious-Exponential-1Hour"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--deployment-duration-in-minutes&lt;/span&gt; 60 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--growth-type&lt;/span&gt; EXPONENTIAL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--growth-factor&lt;/span&gt; 10 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--final-bake-time-in-minutes&lt;/span&gt; 60 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--replicate-to&lt;/span&gt; NONE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same strategy in AWS CDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;appconfig&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-appconfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeploymentStrategyStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&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="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DeploymentStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CautiousExponential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;deploymentStrategyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cautious-Exponential-1Hour&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;rolloutStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RolloutStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exponential&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;growthFactor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;deploymentDuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;finalBakeTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&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;The four settings map directly onto the parameters. In the CLI they're &lt;code&gt;--growth-type&lt;/code&gt;, &lt;code&gt;--growth-factor&lt;/code&gt;, &lt;code&gt;--deployment-duration-in-minutes&lt;/code&gt;, and &lt;code&gt;--final-bake-time-in-minutes&lt;/code&gt;. In CDK they're &lt;code&gt;RolloutStrategy.exponential&lt;/code&gt; with &lt;code&gt;growthFactor&lt;/code&gt;, &lt;code&gt;deploymentDuration&lt;/code&gt;, and &lt;code&gt;finalBakeTime&lt;/code&gt;. The &lt;code&gt;--replicate-to&lt;/code&gt; flag isn't a rollout setting. It's optional, and it controls whether AppConfig also saves the strategy as an AWS Systems Manager document. &lt;code&gt;NONE&lt;/code&gt; keeps it in AppConfig only. Once created, a custom strategy is selectable anywhere a predefined one is: the console, CLI, API, or infrastructure code.&lt;/p&gt;

&lt;p&gt;That snippet is a complete AWS CDK stack. Drop it into a CDK app, run &lt;code&gt;cdk synth&lt;/code&gt;, and deploy it as-is to create the strategy. The posts that follow in this series build on the same pattern, each one standing up its piece with a self-contained stack you can paste in and deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a strategy
&lt;/h2&gt;

&lt;p&gt;There's no universally correct strategy. The right one depends on how much risk a given change carries, how many targets you're rolling it out to, and how quickly your metrics react. A few guidelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Higher-risk changes deserve a slower ramp and a longer bake.&lt;/strong&gt; Say a bad value would be expensive: a pricing flag, a change to an auth path, anything touching data. Lean toward exponential growth and a generous bake time. Small early exposure plus a long watch is exactly what you want when the downside is large.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Larger fleets let you start smaller.&lt;/strong&gt; With thousands of targets, a 1% to 2% first step still covers enough sessions to produce a real signal, and exponential growth lets you start that small without dozens of steps to reach 100%. With a fleet of twenty, a 2% first step probably reaches no one, so a bigger step, or linear growth, makes more sense.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make each step longer than your metrics' reaction time.&lt;/strong&gt; Some signals, such as latency percentiles or error rates averaged over a window, take minutes to appear. When they do, the next step starts before the last one has been judged, unless the step interval outlasts that lag. The interval is deployment time divided by the number of steps. A rollout faster than your metrics is flying blind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size the bake time for problems that only appear at full exposure.&lt;/strong&gt; Queue backlogs, cache churn, and hourly jobs may not register until everyone is on the new value. The bake window is the only monitored time you get after 100%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reserve fast strategies for low-stakes changes, emergencies, or non-production.&lt;/strong&gt; &lt;code&gt;AllAtOnce&lt;/code&gt; is fine for a copy tweak behind a flag, and it's the right call for a kill switch you need everywhere now. It's the wrong one for anything that could take the system down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two constraints shape all of this. First, you choose the strategy per deployment, so nothing forces you to use the same one for a routine flag flip and an emergency change. Second, an environment accepts one deployment at a time. While a two-hour canary is in flight, the next change waits until the canary completes, unless you stop the canary to make room, which rolls it back. That's the real cost of a long strategy, so decide in advance what you'll do if an urgent change lands mid-rollout.&lt;/p&gt;

&lt;p&gt;The habit to build is to ask, for every configuration change: &lt;em&gt;if this value is wrong, how much do I want exposed before I find out?&lt;/em&gt; The answer picks your strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Configuration changes feel low-stakes because they're fast and code-free, and that's exactly why they're dangerous. A deployment strategy puts a governor on that speed. Instead of arriving everywhere at once, a change arrives a slice at a time, under observation, with a monitored window at the end. The cost is time: minutes for the recommended strategies, hours for a cautious custom one, during which the environment is committed to that deployment. In return, a bad value is contained to a fraction of your fleet instead of all of it, your metrics get time to catch it, and, with alarms on the environment, AppConfig reverses it without waiting for a human.&lt;/p&gt;

&lt;p&gt;The core habit is simple: change configuration slowly, and watch the metrics while you do. The rest of safe configuration management builds on that foundation. You validate values before they deploy, alarm on the right metrics, and plan for emergency changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-deployment-strategy.html" rel="noopener noreferrer"&gt;Working with deployment strategies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-deployment-strategy-predefined.html" rel="noopener noreferrer"&gt;Using predefined deployment strategies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/2019-10-09/APIReference/API_CreateDeploymentStrategy.html" rel="noopener noreferrer"&gt;CreateDeploymentStrategy API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-deploying.html" rel="noopener noreferrer"&gt;Deploying a configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-creating-environment.html" rel="noopener noreferrer"&gt;Creating environments&lt;/a&gt; (where you attach CloudWatch alarms)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/monitoring-deployments.html" rel="noopener noreferrer"&gt;Monitoring deployments for automatic rollback&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>appconfig</category>
      <category>devops</category>
      <category>featureflags</category>
    </item>
    <item>
      <title>Why Consolidating Feature Flags and Experiments Cuts Hidden Costs</title>
      <dc:creator>Aparna Krishnamoorthy</dc:creator>
      <pubDate>Mon, 05 Oct 2026 13:50:29 +0000</pubDate>
      <link>https://dev.to/aparkris/why-consolidating-feature-flags-and-experiments-cuts-hidden-costs-2609</link>
      <guid>https://dev.to/aparkris/why-consolidating-feature-flags-and-experiments-cuts-hidden-costs-2609</guid>
      <description>&lt;p&gt;Feature flags and experiments do two different jobs.&lt;/p&gt;

&lt;p&gt;A flag controls &lt;strong&gt;delivery&lt;/strong&gt;. It decides whether a feature is on for a given user, and lets you switch it off fast when something breaks.&lt;/p&gt;

&lt;p&gt;An experiment is about &lt;strong&gt;measurement&lt;/strong&gt;. Split users into groups, show them different variations, and find out which performs better on a metric that matters.&lt;/p&gt;

&lt;p&gt;Take a new checkout button. The flag delivers it: on for these users, off for those. The experiment measures it: does the new button convert better than the old one? Both halves describe a single idea — &lt;em&gt;try a different checkout button and see if it helps&lt;/em&gt; — but because the jobs are different, teams run them in different places. Delivery lives in a flagging tool or a config service. Measurement lives somewhere else: a dedicated A/B platform, or a home-grown mix of an SDK and an analytics pipeline.&lt;/p&gt;

&lt;p&gt;Describe that one idea in two systems, and keeping them in agreement about which users are in which group becomes your job, not the tooling's. That's the seam — the join between delivery and measurement — and it carries a cost in money and engineering time that's easy to overlook. Now that AWS AppConfig runs A/B tests natively on the same feature flags you already deploy, the seam is worth a second look. This post is about the hidden tax of keeping delivery and measurement apart, and what you get back when they share one control plane.&lt;/p&gt;

&lt;h2&gt;
  
  
  The multi-service tax
&lt;/h2&gt;

&lt;p&gt;You pay in four ways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration.&lt;/strong&gt; Two systems to wire into your application, each with its own setup, authentication, and configuration. Two things to provision and keep correct across every environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SDK sprawl.&lt;/strong&gt; A flag-evaluation SDK here, an experiment or analytics SDK there, multiplied across every language and every service you run. More dependencies to ship, more versions to keep current, more surface area when something breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operational overhead.&lt;/strong&gt; Two control planes. Two sets of dashboards. Two permission models to manage and audit. Two things every engineer has to learn before they can safely change what customers see. And the gap between "what's flagged" and "what's being measured" becomes its own category of confusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assignment drift.&lt;/strong&gt; This is the subtle one. When a flagging system and an experiment system bucket users independently, keeping a single user in a consistent variation across both gets fiddly. A small mistake here quietly pollutes your experiment data. It's the worst kind of bug, because nothing errors — you just end up deciding on bad numbers.&lt;/p&gt;

&lt;p&gt;None of these is dramatic on its own. Together they're a standing tax on every change worth measuring.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;In mid-2026, AWS announced the general availability of experimentation tools in AWS AppConfig. A/B and multivariate testing are now built into the feature flags you already deploy, with no separate experimentation infrastructure to build or run. You define variations, target an audience with a rule builder, set the traffic split, ramp exposure, and promote the winner — all anchored to the flag itself — through the console, CLI, API, or CDK. It's available in all commercial AWS Regions.&lt;/p&gt;

&lt;p&gt;The timing matters for a second reason. Amazon CloudWatch Evidently, AWS's dedicated A/B testing service, reached end of support on October 17, 2025. So "where does A/B testing live now?" has been a live question for anyone who relied on Evidently, not a hypothetical one.&lt;/p&gt;

&lt;p&gt;The practical effect is simple: the flag that delivers a feature is now the same flag that runs the experiment measuring it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get back
&lt;/h2&gt;

&lt;p&gt;Walk back through the same four costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One control plane.&lt;/strong&gt; The flag &lt;em&gt;is&lt;/em&gt; the experiment surface. An experiment selects an existing feature flag, and each treatment is a variation of that flag's configuration. The thing that controls the rollout is the thing that runs the test, and defining a variation and its traffic split is just part of the experiment definition. Here it is in the CDK, where each treatment carries the flag values it sets and the &lt;code&gt;weight&lt;/code&gt; that splits traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aws_appconfig&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;appconfig&lt;/span&gt;

&lt;span class="n"&gt;AttrValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CfnExperimentDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AttributeValueProperty&lt;/span&gt;
&lt;span class="n"&gt;Treatment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CfnExperimentDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TreatmentProperty&lt;/span&gt;

&lt;span class="c1"&gt;# app, env, and profile are the L1 CfnApplication, CfnEnvironment, and
# CfnConfigurationProfile for your feature flags, defined elsewhere in the stack.
# The checkout_button flag must already define a "color" attribute. Treatments
# set the value of an attribute the flag defines; they don't create new ones.
&lt;/span&gt;&lt;span class="n"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnExperimentDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CheckoutButtonExperiment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checkout-button-color&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;application_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;environment_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;configuration_profile_identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;flag_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checkout_button&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# Editor-syntax expression. $country is a caller-supplied context attribute,
&lt;/span&gt;    &lt;span class="c1"&gt;# not an AWS Region; here, everyone in the US is eligible.
&lt;/span&gt;    &lt;span class="n"&gt;audience_rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(eq $country &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# Baseline: today's blue button, half the traffic. AppConfig generates the
&lt;/span&gt;    &lt;span class="c1"&gt;# treatment key; the Agent returns it as "_variant". The control comes back
&lt;/span&gt;    &lt;span class="c1"&gt;# as "__control__" and treatments as "__t1__", "__t2__", and so on.
&lt;/span&gt;    &lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Treatment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;attribute_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;color&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AttrValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blue&lt;/span&gt;&lt;span class="sh"&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;# Variation under test: green button, the other half.
&lt;/span&gt;    &lt;span class="n"&gt;treatments&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Treatment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;attribute_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;color&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AttrValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;green&lt;/span&gt;&lt;span class="sh"&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;The &lt;code&gt;CfnExperimentDefinition&lt;/code&gt; construct is only in recent versions of aws-cdk-lib (2.268.0 or later).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One agent.&lt;/strong&gt; Experiments are delivered through the AWS AppConfig Agent, which runs on Amazon EC2, AWS Lambda, Amazon ECS, Amazon EKS, and on-premises servers. If you already use the agent to evaluate flags, that's one component per runtime to ship and keep current, not two. If you currently call the AppConfig API directly, budget for adding the agent: experimentation requires it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One workflow.&lt;/strong&gt; Define your variations, target the audience, set the traffic split, then start at 0% exposure and ramp up while watching your metrics. When a treatment wins, you promote it through a standard AppConfig safe rollout — the same gradual, monitored deployment you'd use for any other configuration change. One detail worth getting right: update and deploy the flag with the winning values &lt;em&gt;while the experiment is still running&lt;/em&gt;, then stop it. In that order, users go straight from the split to the full rollout. Stop first, and the flag reverts to whatever is currently deployed (usually the default) until you redeploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistent assignment by construction.&lt;/strong&gt; The system doing the flagging is the system doing the bucketing. There's no second service to disagree with, so "did both systems put this user in the same group?" stops being a question you can get wrong. It isn't zero work. You retrieve a treatment with the same agent call you already use for flags, plus two headers: an &lt;code&gt;Entity-Id&lt;/code&gt; that identifies the user, and a &lt;code&gt;Context&lt;/code&gt; that carries the attributes your audience rule evaluates. But given a stable entity ID, the agent returns the same treatment for that entity for the life of the run — consistency is the system's job, not yours. And because treatment allocations lock once a run starts, the split can't quietly shift underneath you partway through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the savings stop
&lt;/h2&gt;

&lt;p&gt;Consolidation covers delivery, assignment, and exposure control. Analysis is still yours. Configuring an analytics platform to capture experiment data is an explicit step in the workflow, and AppConfig is deliberately agnostic about which one: CloudWatch, Snowflake, Datadog, or your own warehouse. You're unifying &lt;em&gt;where experiments run&lt;/em&gt;, not &lt;em&gt;how you measure them&lt;/em&gt;. That's the right trade — your metric definitions are usually the last thing you'd want to migrate — but it does mean the analytics side stays with you.&lt;/p&gt;

&lt;p&gt;It also isn't free. AppConfig experimentation is pay-as-you-go, billed per experiment-run hour from the moment a run starts until you end it, and standard AppConfig configuration-request charges still apply on top while the experiment runs. (See the pricing link below for current rates.) The argument isn't that consolidation costs nothing. It's that you stop paying twice for one idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  When consolidating isn't the answer
&lt;/h2&gt;

&lt;p&gt;This isn't a case for ripping out a system that's serving you well.&lt;/p&gt;

&lt;p&gt;If you've already invested in a dedicated experimentation platform — one your analysts have built tooling around, with its own statistics engine, holdout groups, and a workflow people rely on — the math is different. AppConfig's AI-assisted experiment design validates your setup against experimentation best practices at design time, which helps you reach sufficient statistical power, but the analysis itself happens in your own tools, and very advanced programs may still want a platform that does both.&lt;/p&gt;

&lt;p&gt;The same goes if your flags are evaluated in a browser or mobile client. The documented path runs through the agent on server-side compute, so client-side experiments mean your backend does the assignment and hands the result down. That's a design decision, not a blocker, but one to make deliberately.&lt;/p&gt;

&lt;p&gt;The value of consolidation is highest when your second system is mostly overhead — when it exists only because flagging and experimentation historically couldn't share a home. The case is strongest for teams already running AppConfig feature flags, where a separate experiment system duplicates flags they already have.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick gut check
&lt;/h2&gt;

&lt;p&gt;Four questions to size up your own tax:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you deliver a feature in one system and measure it in another, describing the same change twice?&lt;/li&gt;
&lt;li&gt;How many SDKs or agents does a single feature touch as it evaluates end to end?&lt;/li&gt;
&lt;li&gt;When an experiment produces a winner, can you promote it through your existing deployment safety net, or is that a separate manual step?&lt;/li&gt;
&lt;li&gt;Is keeping a user in a consistent variation across systems something you actively babysit?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more "yes" answers, the more there is to reclaim.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;For teams building on AWS, the split between delivering a feature and measuring it used to be structural. AppConfig flagged, something else experimented, and stitching the two together was the cost of doing business.&lt;/p&gt;

&lt;p&gt;It isn't structural anymore. It's a choice — and consolidating saves more than dollars: fewer moving parts, less drift, and one safe path from testing an idea to shipping the winner. It's the kind of cost that's easy to keep paying precisely because no invoice ever arrives for it, which is exactly why it's worth adding up on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-experimentation.html" rel="noopener noreferrer"&gt;AWS AppConfig experimentation (User Guide)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/6/aws-appconfig-experimentation/" rel="noopener noreferrer"&gt;AWS AppConfig launches managed experimentation tools for A/B testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-experimentation-example-flow.html" rel="noopener noreferrer"&gt;Example experiment workflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-experimentation-promoting-a-treatment.html" rel="noopener noreferrer"&gt;Promoting a winning treatment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/systems-manager/pricing/" rel="noopener noreferrer"&gt;AWS AppConfig pricing&lt;/a&gt; (AppConfig appears on the AWS Systems Manager pricing page; experimentation is billed per experiment-run hour)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-experimentation-terminology.html" rel="noopener noreferrer"&gt;Experiment-run pricing model, explained&lt;/a&gt; (see the "Experiment run" definition)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/mt/support-for-amazon-cloudwatch-evidently-ending-soon/" rel="noopener noreferrer"&gt;Support for Amazon CloudWatch Evidently ending soon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>aws</category>
      <category>ai</category>
      <category>experimentation</category>
    </item>
    <item>
      <title>Stop hardcoding AWS Lambda layer ARNs: use public parameters for the AWS Parameters and Secrets Lambda Extension</title>
      <dc:creator>Aparna Krishnamoorthy</dc:creator>
      <pubDate>Tue, 15 Sep 2026 13:09:51 +0000</pubDate>
      <link>https://dev.to/aparkris/stop-hardcoding-aws-lambda-layer-arns-use-public-parameters-for-the-aws-parameters-and-secrets-56m5</link>
      <guid>https://dev.to/aparkris/stop-hardcoding-aws-lambda-layer-arns-use-public-parameters-for-the-aws-parameters-and-secrets-56m5</guid>
      <description>&lt;p&gt;To add the AWS Parameters and Secrets Lambda Extension to an AWS Lambda function, you can open the documentation, find the ARN table, locate your AWS Region and architecture, copy the ARN, and paste it into your template. This approach works, but the version number at the end of that ARN changes every time AWS releases an update. If you hardcode that number, your deployment stays pinned to an older version, which you might not notice for months.&lt;/p&gt;

&lt;p&gt;There's a better approach that uses public parameters in AWS Systems Manager Parameter Store (Parameter Store).&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the AWS Parameters and Secrets Lambda Extension?
&lt;/h2&gt;

&lt;p&gt;The AWS Parameters and Secrets Lambda Extension is a Lambda layer that retrieves and caches values from Parameter Store and AWS Secrets Manager. It runs as a companion process alongside your function, and serves cached parameter values over a local HTTP endpoint. The use of this extension reduces latency, reduces API call costs, and simplifies your function code: you make a local HTTP call instead of configuring an SDK client and managing your own caching logic.&lt;/p&gt;

&lt;p&gt;If you're new to the extension, the &lt;a href="https://aws.amazon.com/blogs/compute/using-the-aws-parameter-and-secrets-lambda-extension-to-cache-parameters-and-secrets/" rel="noopener noreferrer"&gt;AWS Compute Blog post&lt;/a&gt; covers the full setup, and provides a walkthrough with load test results showing ~98% fewer API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with hardcoded layer ARNs
&lt;/h2&gt;

&lt;p&gt;The extension is distributed as a Lambda layer. To attach it, you need its ARN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:122
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The version number at the end (&lt;code&gt;122&lt;/code&gt; at the time of writing) changes with each release. If you hardcode it, it'll keep working, but you won't get bug fixes, performance improvements, or new features until someone manually checks the docs and updates it.&lt;/p&gt;

&lt;p&gt;The Lambda console makes this easy for a single function. You pick "AWS-Parameters-and-Secrets-Lambda-Extension" from a dropdown and choose the latest version. But that doesn't help your CloudFormation templates, CDK stacks, or Terraform configurations, where the ARN is a static string that someone has to maintain.&lt;/p&gt;

&lt;p&gt;For a team managing many functions across multiple AWS Regions, it becomes a maintenance chore that's easy to forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better approach: resolve at deploy time
&lt;/h2&gt;

&lt;p&gt;AWS publishes the latest extension layer ARN as a public parameter in every commercial AWS Region. The paths are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;th&gt;Parameter path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x86_64&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/aws/service/aws-parameters-and-secrets-lambda-extension/x86/latest&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARM64&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/aws/service/aws-parameters-and-secrets-lambda-extension/arm64/latest&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When you reference these parameters in infrastructure code, the ARN resolves at deploy time. Each deployment picks up the latest version automatically.&lt;/p&gt;

&lt;p&gt;The only IAM permission you need is &lt;code&gt;ssm:GetParameter&lt;/code&gt;. The parameters are public and readable from any AWS account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieving the ARN from the CLI
&lt;/h2&gt;

&lt;p&gt;To see the latest layer ARN for your AWS Region, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ssm get-parameter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/aws/service/aws-parameters-and-secrets-lambda-extension/x86/latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Parameter.Value"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For ARM64:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ssm get-parameter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/aws/service/aws-parameters-and-secrets-lambda-extension/arm64/latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Parameter.Value"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use these commands in a script, a CI pipeline, or to check what version is the latest before deploying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the parameter in CloudFormation
&lt;/h2&gt;

&lt;p&gt;CloudFormation supports dynamic references that resolve SSM parameters during stack operations. If you reference the public parameter directly, your template won't need a version-specific ARN:&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;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MyFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-function&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.12&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
      &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;x86_64&lt;/span&gt;
      &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ZipFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;import json&lt;/span&gt;
          &lt;span class="s"&gt;import os&lt;/span&gt;
          &lt;span class="s"&gt;import urllib.request&lt;/span&gt;

          &lt;span class="s"&gt;def handler(event, context):&lt;/span&gt;
              &lt;span class="s"&gt;token = os.environ['AWS_SESSION_TOKEN']&lt;/span&gt;

              &lt;span class="s"&gt;req = urllib.request.Request(&lt;/span&gt;
                  &lt;span class="s"&gt;'http://localhost:2773/systemsmanager/parameters/get?name=%2Fmy%2Fparameter'&lt;/span&gt;
              &lt;span class="s"&gt;)&lt;/span&gt;
              &lt;span class="s"&gt;req.add_header('X-Aws-Parameters-Secrets-Token', token)&lt;/span&gt;
              &lt;span class="s"&gt;config = urllib.request.urlopen(req).read()&lt;/span&gt;
              &lt;span class="s"&gt;return json.loads(config)&lt;/span&gt;
      &lt;span class="na"&gt;Layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{resolve:ssm:/aws/service/aws-parameters-and-secrets-lambda-extension/x86/latest}}'&lt;/span&gt;
      &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;MyFunctionRole.Arn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;{{resolve:ssm:...}}&lt;/code&gt; syntax tells CloudFormation to fetch the parameter value during stack creation or update. The resolved value is fixed until the next stack operation. To pick up a new extension version, update the stack. (The template itself doesn't change.)&lt;/p&gt;

&lt;p&gt;For ARM64 functions, swap the path:&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;Layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{resolve:ssm:/aws/service/aws-parameters-and-secrets-lambda-extension/arm64/latest}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the parameter in CDK
&lt;/h2&gt;

&lt;p&gt;CDK resolves the parameter at deploy time using the same underlying SSM resolution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ssm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-ssm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ParameterCacheStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&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="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extensionArn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ssm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StringParameter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valueForStringParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/aws/service/aws-parameters-and-secrets-lambda-extension/x86/latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PYTHON_3_12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LayerVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLayerVersionArn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ParamsSecretsExt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extensionArn&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;Under the hood, CDK synthesizes this into a CloudFormation template parameter of type &lt;code&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;&lt;/code&gt;, so the layer ARN is resolved from the public parameter at deploy time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the parameter in Terraform
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"params_secrets_extension"&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;"/aws/service/aws-parameters-and-secrets-lambda-extension/x86/latest"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"my_function"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-function"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.12"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"index.handler"&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda.zip"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_exec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;

  &lt;span class="nx"&gt;architectures&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"x86_64"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;layers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ssm_parameter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params_secrets_extension&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each &lt;code&gt;terraform apply&lt;/code&gt; resolves the parameter. If a new version was published since your last deploy, you'd see it in the plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  A common mistake: parameter ARN vs. parameter value
&lt;/h2&gt;

&lt;p&gt;Because it's an AWS resource, the SSM public parameter has its own ARN, and it contains a value that is the Lambda layer ARN. These are not the same thing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parameter ARN&lt;/strong&gt; (the address): &lt;code&gt;arn:aws:ssm:us-east-1::parameter/aws/service/aws-parameters-and-secrets-lambda-extension/x86/latest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter value&lt;/strong&gt; (the content): &lt;code&gt;arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:122&lt;/code&gt; (version number at the time of writing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you paste the parameter ARN into the Lambda layer field in the console, you'll get a validation error. Lambda expects a layer ARN, not a parameter ARN. The dynamic references in CloudFormation and Terraform handle this resolution automatically. However, if you copy manually from the console, make sure you're copying the &lt;strong&gt;value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One more detail the parameters save you from: the x86_64 and ARM64 layers have different names, not just different version numbers. The x86_64 value resolves to a layer named &lt;code&gt;AWS-Parameters-and-Secrets-Lambda-Extension&lt;/code&gt;, while the ARM64 value resolves to &lt;code&gt;AWS-Parameters-and-Secrets-Lambda-Extension-Arm64&lt;/code&gt;. Reference the right architecture's parameter and you never have to track that distinction yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters for teams
&lt;/h2&gt;

&lt;p&gt;Public parameters reduce maintenance work. Every team's deployment pipeline resolves the latest version independently. You don't need shared spreadsheets of ARNs, Slack messages asking what the latest version is, or pull requests that just bump a version number.&lt;/p&gt;

&lt;p&gt;When AWS publishes an update with a bug fix or performance improvement, your next deployment picks it up automatically without the need for coordination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html" rel="noopener noreferrer"&gt;Using Parameter Store parameters in AWS Lambda functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/compute/using-the-aws-parameter-and-secrets-lambda-extension-to-cache-parameters-and-secrets/" rel="noopener noreferrer"&gt;Using the AWS Parameter and Secrets Lambda extension to cache parameters and secrets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-finding-public-parameters.html" rel="noopener noreferrer"&gt;Discovering public parameters in Parameter Store&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>parameterstore</category>
      <category>lambda</category>
      <category>devops</category>
    </item>
    <item>
      <title>Stop hardcoding AWS Lambda layer ARNs, and use AWS Systems Manager Parameter Store public parameters instead</title>
      <dc:creator>Aparna Krishnamoorthy</dc:creator>
      <pubDate>Tue, 01 Sep 2026 03:43:00 +0000</pubDate>
      <link>https://dev.to/aparkris/-stop-hardcoding-aws-lambda-layer-arns-and-use-aws-systems-manager-parameter-store-public-p70</link>
      <guid>https://dev.to/aparkris/-stop-hardcoding-aws-lambda-layer-arns-and-use-aws-systems-manager-parameter-store-public-p70</guid>
      <description>&lt;p&gt;To add the AWS AppConfig Agent Lambda extension to an AWS Lambda function, you can open the documentation, scroll through a table of ARNs, find the one that matches your AWS Region and architecture, copy it, and paste it into your template. However, a few months later, AWS publishes a new version and now your deployment is silently using an older one. There’s a better approach that uses public parameters in AWS Systems Manager Parameter Store (Parameter Store).&lt;/p&gt;

&lt;h2&gt;
  
  
  What are public parameters in Parameter Store?
&lt;/h2&gt;

&lt;p&gt;Many AWS services use Parameter Store to publish read-only public parameters with names that start with &lt;code&gt;/aws/service/{service-name}&lt;/code&gt;. These public parameters contain up-to-date metadata about AWS services.&lt;/p&gt;

&lt;p&gt;You've probably seen them used for AMI lookups for fetching the latest Amazon Linux AMI ID without hardcoding it. The same mechanism is available for Lambda layer ARNs, ECS-optimized AMIs, and other resources that AWS updates regularly.&lt;/p&gt;

&lt;p&gt;The key idea is that instead of looking up a value in documentation and pasting it into your code, you query Parameter Store at deploy time and get the current value. The only IAM permission that's required is &lt;code&gt;ssm:GetParameter&lt;/code&gt;. The parameters are public and readable from any AWS account.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with hardcoded layer ARNs
&lt;/h2&gt;

&lt;p&gt;The AWS AppConfig Agent Lambda extension is distributed as a Lambda layer. To attach it, you need the layer's ARN, which includes a version number at the end like in this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:lambda:us-east-1:027255383542:layer:AWS-AppConfig-Extension:128
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That version number changes every time AWS releases an update. If you hardcode it, you get a working deployment, but you also get silent drift. A few months from now, you'll be running an older version without realizing it.&lt;/p&gt;

&lt;p&gt;Imagine a team that's managing dozens of functions in multiple AWS Regions, and you can see how this can become a maintenance problem. Someone has to regularly check the documentation, update the ARN, and redeploy. It's not difficult work, but it's the kind of thing that's a hassle and that gets forgotten.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better approach: resolve at deploy time
&lt;/h2&gt;

&lt;p&gt;AWS publishes the latest AppConfig extension layer ARN as a public parameter in every commercial AWS Region. Here are the two paths:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;th&gt;Parameter path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x86_64&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/aws/service/aws-appconfig/lambda-extension/x86/latest&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARM64&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/aws/service/aws-appconfig/lambda-extension/arm64/latest&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The x86 path uses &lt;code&gt;x86&lt;/code&gt;, not &lt;code&gt;x86_64&lt;/code&gt;, even though the Lambda architecture name is &lt;code&gt;x86_64&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you reference these parameters in your infrastructure code, each deployment resolves the current value from Parameter Store. How and when that resolution happens depends on your tooling. The AWS CLI resolves the parameter on each invocation. CloudFormation dynamic references (&lt;code&gt;{{resolve:ssm:...}}&lt;/code&gt;) and parameter types (&lt;code&gt;AWS::SSM::Parameter::Value&lt;/code&gt;) resolve during stack create or update operations. The resolved value is then fixed until the next stack operation. Either way, you avoid hardcoding and pick up new versions through your normal deployment workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieving the ARN from the CLI
&lt;/h2&gt;

&lt;p&gt;As an example, run the following AWS CLI command. It returns the full layer ARN for your current AWS Region.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ssm get-parameter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/aws/service/aws-appconfig/lambda-extension/x86/latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Parameter.Value"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For ARM64:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ssm get-parameter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/aws/service/aws-appconfig/lambda-extension/arm64/latest"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Parameter.Value"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the parameter in CloudFormation
&lt;/h2&gt;

&lt;p&gt;You can reference the public parameter directly in a CloudFormation template using a dynamic reference:&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;MyFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-function&lt;/span&gt;
    &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.12&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
    &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;x86_64&lt;/span&gt;
    &lt;span class="na"&gt;Layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{resolve:ssm:/aws/service/aws-appconfig/lambda-extension/x86/latest}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this approach matters for teams
&lt;/h2&gt;

&lt;p&gt;For one developer working on one function, looking up an ARN in the documentation is fine. But when you have multiple teams deploying functions across AWS Regions, keeping layer versions current becomes a coordination problem.&lt;/p&gt;

&lt;p&gt;Public parameters remove that concern. Every team's deployment pipeline resolves the latest version independently. You don't need shared spreadsheets of ARNs or Slack messages asking if anyone has checked the latest version. It's a small thing, but small operational improvements add up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-finding-public-parameters.html" rel="noopener noreferrer"&gt;Discovering public parameters in Parameter Store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-integration-lambda-extensions-versions.html" rel="noopener noreferrer"&gt;Available versions of the AWS AppConfig Agent Lambda extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Finding public SSM parameters for AMIs with the EC2 DescribeImages API</title>
      <dc:creator>Aparna Krishnamoorthy</dc:creator>
      <pubDate>Wed, 19 Aug 2026 13:49:48 +0000</pubDate>
      <link>https://dev.to/aparkris/finding-public-ssm-parameters-for-amis-with-the-ec2-describeimages-api-6e5</link>
      <guid>https://dev.to/aparkris/finding-public-ssm-parameters-for-amis-with-the-ec2-describeimages-api-6e5</guid>
      <description>&lt;p&gt;Services like EC2 and ECS require an AMI ID at launch time, which pins the instance to a specific OS image. AMIs from AWS and its partners get patched often, and each patch ships as a new ID. If you hardcode that ID, it goes stale quickly and your infrastructure drifts behind the latest patches.&lt;/p&gt;

&lt;p&gt;The standard fix is to use a public SSM parameter — a well-known path that always resolves to the current AMI in a given family. That works great for new deployments, but what about existing code where the AMI ID is already baked in? Finding the right parameter used to mean navigating cryptic /aws/service/ hierarchies, guessing naming conventions, and making dozens of &lt;code&gt;GetParametersByPath&lt;/code&gt;calls. This tedious process not only slowed down workflows but also introduced the risk of selecting the wrong parameter, potentially leading to deployment issues.&lt;/p&gt;

&lt;p&gt;A new EC2 feature eliminates that friction: the &lt;code&gt;DescribeImages&lt;/code&gt; API now includes a &lt;code&gt;PublicSsmParameterName&lt;/code&gt; field that tells you exactly which parameter maps to a given AMI. &lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the SSM parameter for a public AMI in DescribeImages
&lt;/h2&gt;

&lt;p&gt;You can avoid the manual search by retrieving the parameter name from &lt;code&gt;DescribeImages&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 describe-images &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--image-ids&lt;/span&gt; ami-1234567890abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Images[*].{ImageId:ImageId,PublicSsmParameterName:PublicSsmParameterName}'&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;
 &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="s2"&gt;"ImageId"&lt;/span&gt;: &lt;span class="s2"&gt;"ami-1234567890abcdef0"&lt;/span&gt;,
 &lt;span class="s2"&gt;"PublicSsmParameterName"&lt;/span&gt;: &lt;span class="s2"&gt;"aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The parameter will now resolve to the updated AMI than the one you described if the publisher has released a newer version. &lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the leading slash before using the parameter
&lt;/h2&gt;

&lt;p&gt;When you copy the parameter name from the EC2 &lt;code&gt;DescribeImages&lt;/code&gt; API, note that it doesn't have the leading slash required by Parameter Store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ssm get-parameter &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
ValidationException: Parameter name: can't be prefixed with "ssm"...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a leading / to avoid the error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ssm get-parameter &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using a parameter instead of a hardcoded AMI ID
&lt;/h2&gt;

&lt;p&gt;Reference the parameter returned by &lt;code&gt;DescribeImages&lt;/code&gt; directly in your infrastructure code.&lt;/p&gt;

&lt;p&gt;In the following CloudFormation example, you define a parameter of type &lt;code&gt;AWS::SSM::Parameter::Value&amp;lt;AWS::EC2::Image::Id&amp;gt;&lt;/code&gt;, and use it for the &lt;code&gt;ImageId&lt;/code&gt; property.&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;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;LatestAmiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter::Value&amp;lt;AWS::EC2::Image::Id&amp;gt;&lt;/span&gt;
 &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;Instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::EC2::Instance&lt;/span&gt;
 &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;ImageId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;LatestAmiId&lt;/span&gt;
 &lt;span class="na"&gt;InstanceType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;t3.micro&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In the following CDK example, you pass the same parameter name to &lt;code&gt;MachineImage.fromSsmParameter()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;machineImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MachineImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromSsmParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Instance&lt;/span&gt;&lt;span class="dl"&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;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;instanceType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InstanceType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InstanceClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InstanceSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MICRO&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="nx"&gt;machineImage&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 resolve the parameter when you create or update the stack, not on every new instance launch, so an update is still reviewable and tied to your normal deployment process.&lt;/p&gt;

&lt;p&gt;In the following example, you use the &lt;code&gt;resolve:ssm:&lt;/code&gt; prefix on the &lt;code&gt;ImageId&lt;/code&gt;. New instances resolve the parameter at launch time, so they can use the current AMI in that AMI family without a redeploy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 create-launch-template &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--launch-template-name&lt;/span&gt; my-template &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--version-description&lt;/span&gt; version1 &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;--launch-template-data&lt;/span&gt; &lt;span class="s1"&gt;'{"ImageId":"resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64","InstanceType":"t3.micro"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;DescribeImages&lt;/code&gt; returns the public SSM parameter associated with a public AMI family. Reference that parameter in your launch and deployment code to avoid manually searching for it. Remember to add the leading slash to the parameter name when retrieving it from Parameter Store.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>parameterstore</category>
      <category>ec2</category>
    </item>
    <item>
      <title>Scaling Parameter Store reads for production workloads</title>
      <dc:creator>Aparna Krishnamoorthy</dc:creator>
      <pubDate>Thu, 23 Jul 2026 19:33:35 +0000</pubDate>
      <link>https://dev.to/aparkris/scaling-parameter-store-reads-for-production-workloads-46ie</link>
      <guid>https://dev.to/aparkris/scaling-parameter-store-reads-for-production-workloads-46ie</guid>
      <description>&lt;p&gt;Consider an order-processing service running on Amazon ECS Fargate during a weekend sale. Under normal conditions, 20 tasks read configuration values from Parameter Store at startup. As traffic increases, the service scales to 200 tasks, causing many tasks to request the same parameters at nearly the same time. If these requests exceed the available throughput, new tasks can start slowly or fail, potentially delaying or interrupting customer orders.&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%2Fsyheu0gk10nqclytk0a6.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%2Fsyheu0gk10nqclytk0a6.png" alt="Parameter Store Throughput" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When an application exceeds the available quota, its logs can show a &lt;code&gt;ThrottlingException&lt;/code&gt;or &lt;code&gt;RateExceeded&lt;/code&gt; error from &lt;code&gt;GetParameter&lt;/code&gt;, &lt;code&gt;GetParameters&lt;/code&gt;, or &lt;code&gt;GetParametersByPath&lt;/code&gt;. To diagnose such issues, you can check the error source in application logs, or trace the relevant API calls in CloudTrail. Then compare the timing against a known scaling or deployment event. Throttling or latency issues that cluster around this event are a strong signal that concurrent parameter requests caused the bottleneck. &lt;/p&gt;

&lt;p&gt;You can address Parameter Store throughput issues in the following complementary ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable higher throughput.&lt;/strong&gt; Increase the quota so that production workloads handle concurrent reads during normal operation and scaling events. The cost is based on usage. For example, a workload generating approximately 456,000 parameter interactions for a 60-hour sale costs about $2.28.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize read patterns.&lt;/strong&gt; Reduce unnecessary API traffic by retrieving only the parameters the application needs and caching values that it reads repeatedly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Enabling higher throughput raises the quota for each API action in a single account in a single Region. Throughput for &lt;code&gt;SecureString&lt;/code&gt; parameters might be further limited by AWS KMS. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API action&lt;/th&gt;
&lt;th&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-throughput.html" rel="noopener noreferrer"&gt;Default quota&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-throughput.html" rel="noopener noreferrer"&gt;Higher-throughput quota&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GetParameter&lt;/td&gt;
&lt;td&gt;Shared 40 TPS&lt;/td&gt;
&lt;td&gt;10,000 TPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetParameters&lt;/td&gt;
&lt;td&gt;Shared 40 TPS&lt;/td&gt;
&lt;td&gt;1,000 TPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetParametersByPath&lt;/td&gt;
&lt;td&gt;Shared 40 TPS&lt;/td&gt;
&lt;td&gt;100 TPS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Improving application resilience by enabling higher throughput
&lt;/h2&gt;

&lt;p&gt;Consider enabling higher throughput in the following scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parameter Store is part of a production request path, not a one-time deployment. A higher quota ensures configuration requests don’t become a bottleneck at runtime. &lt;/li&gt;
&lt;li&gt;Many instances, containers, or Lambda functions read parameters within the same window. Higher throughput lets your fleet scale without individual instances competing for a shared, undersized quota. &lt;/li&gt;
&lt;li&gt;Deployments, restarts, or autoscaling events produce bursts of reads. You can absorb these predictable spikes, so a routine deployment doesn't fail.&lt;/li&gt;
&lt;li&gt;Usage regularly approaches or exceeds the default 40 TPS quota. By proactively increasing your quota, you eliminate bottlenecks instead of waiting for them to recur under heavier load.&lt;/li&gt;
&lt;li&gt;You want more throughput but don’t want to redesign the system. Enabling the higher-throughput setting increases your quota immediately without touching your infrastructure or application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you enable higher throughput, you increase the request quota for your AWS account and Region. &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%2F3chxqgjmgn1vo6efo5br.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%2F3chxqgjmgn1vo6efo5br.png" alt="Parameter Store Higher Throughput" width="799" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cost of higher throughput is &lt;a href="https://aws.amazon.com/systems-manager/pricing/" rel="noopener noreferrer"&gt;$0.05 per 10,000 parameter interactions&lt;/a&gt;. A request that retrieves multiple parameters counts as one interaction for each parameter returned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An order-processing example&lt;/strong&gt;&lt;br&gt;
Assume that our order-processing service runs 20 tasks under normal conditions. During a promotional event lasting 60 hours (6 p.m. Friday to 6 a.m. Monday), it scales to 200 tasks for the duration of the sale. To limit stale values, each task refreshes the database endpoint, inventory service, and order dead-letter queue parameters every 5 minutes as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;200 tasks × (60 hours × 12 refreshes per hour) = 144,000 refresh calls.&lt;/li&gt;
&lt;li&gt;144,000 calls × 3 parameters each = 432,000 parameter interactions.&lt;/li&gt;
&lt;li&gt;A Lambda function handling checkout cold starts approximately 8,000 times during traffic bursts, creating 24,000 additional interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is approximately 456,000 parameter interactions for the 60-hour sale. With higher throughput enabled, the application avoids throttling errors caused by hitting quota limits. At $0.05 per 10,000 interactions, the total additional cost is $2.28.&lt;/p&gt;

&lt;p&gt;Because the higher-throughput setting is billed on usage, you can enable it before an anticipated traffic spike and disable it afterward. If your application is throttled during normal production workloads, however, consider enabling higher throughput as a permanent setting.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enabling higher throughput in the AWS console and CLI
&lt;/h2&gt;

&lt;p&gt;A throughput setting applies at the account and Region level. It takes effect for all applications reading from Parameter Store in the specified scope.&lt;/p&gt;

&lt;p&gt;In the console, increase throughput by choosing &lt;strong&gt;Systems Manager &amp;gt; Parameter Store &amp;gt; Settings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the AWS CLI, run the following command to enable higher throughput, replacing the AWS Region and account ID with your own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shell
aws ssm update-service-setting &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--setting-id&lt;/span&gt; arn:aws:ssm:us-east-1:123456789012:servicesetting/ssm/parameter-store/high-throughput-enabled &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--setting-value&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Optimizing read patterns
&lt;/h2&gt;

&lt;p&gt;Optimizing how your application retrieves parameters is an operational best practice. If your application retrieves only necessary parameters and caches values that it reads repeatedly, it reduces latency and uses throughput more efficiently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avoid retrieving more than your application needs.&lt;/strong&gt; Parameter hierarchies are a useful way to organize configuration values. For example, you can group parameters for a checkout application as follows: &lt;code&gt;/prod/checkout/db-host&lt;/code&gt;, &lt;code&gt;/prod/checkout/inventory-service-endpoint&lt;/code&gt;, and &lt;code&gt;/prod/checkout/order-dlq-arn&lt;/code&gt;. It’s tempting to use &lt;code&gt;GetParametersByPath&lt;/code&gt; to retrieve an entire subtree when your application knows the specific parameters it needs.&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="sb"&gt;`&lt;/span&gt;&lt;span class="c"&gt;# Gets the subtree, including parameters your app might not need`&lt;/span&gt;
&lt;span class="sb"&gt;`&lt;/span&gt;aws ssm get-parameters-by-path &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--path&lt;/span&gt; &lt;span class="s2"&gt;"/prod/checkout/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--with-decryption&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With higher throughput enabled, &lt;code&gt;GetParametersByPath&lt;/code&gt; is limited to 100 TPS, compared to 1,000 for &lt;code&gt;GetParameters&lt;/code&gt; and 10,000 for single-parameter &lt;code&gt;GetParameter&lt;/code&gt; calls. With default throughput, all three share the same 40 TPS pool. If your application knows its parameter names, requesting them directly means that your application is less likely to hit the quota.&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="sb"&gt;`&lt;/span&gt;&lt;span class="c"&gt;# Retrieves only the parameters your app actually needs&lt;/span&gt;
aws ssm get-parameters &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--names&lt;/span&gt; &lt;span class="s2"&gt;"/prod/checkout/db-host"&lt;/span&gt; &lt;span class="s2"&gt;"/prod/checkout/inventory-service-endpoint"&lt;/span&gt; &lt;span class="s2"&gt;"/prod/checkout/order-dlq-arn"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--with-decryption&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a best practice, remove unused parameters from a hierarchy. You prevent &lt;code&gt;GetParametersByPath&lt;/code&gt; calls from returning parameters that the application doesn’t need and can reduce the paginated requests for larger hierarchies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache parameter values in the application.&lt;/strong&gt; Many configuration values don’t change often enough to justify reading them on every request. If your application reads the same parameter many times per minute, read it once and reuse the value for an appropriate period. For example, use a short reuse window for values that change often and a longer one for values that don’t. &lt;/p&gt;

&lt;p&gt;The following sample code defines a &lt;code&gt;get_parameter()&lt;/code&gt; function. You can call this function every 10 seconds for a specific parameter. The first call after the five-minute TTL expires retrieves the latest value from Parameter Store and updates the cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;ssm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ssm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;TTL_SECONDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;  &lt;span class="c1"&gt;# how long to trust a cached value
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cached_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;cached_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;TTL_SECONDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WithDecryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Parameter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pname&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Spread out reads when many resources start at the same time.&lt;/strong&gt; A common source of throttling is simultaneous startup. This occurs when many EC2 instances or ECS tasks read the same parameters within the same second during deployment or scaling. You can avoid concentrating the read burst by reading parameters once and passing the values to the application, or by staggering reads with a slight delay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Lambda functions, consider the AWS Parameters and Secrets Lambda Extension.&lt;/strong&gt; The extension caches parameter values locally for reuse across invocations. This reduces both the number of calls to Parameter Store and the latency of retrieving values. &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Your application logs might show &lt;code&gt;ThrottlingException&lt;/code&gt; or &lt;code&gt;RateExceeded&lt;/code&gt;errors from &lt;code&gt;GetParameter&lt;/code&gt;, &lt;code&gt;GetParameters&lt;/code&gt;, or &lt;code&gt;GetParametersByPath&lt;/code&gt;. The results of throttling can be outages and degraded customer experience.&lt;/p&gt;

&lt;p&gt;Enabling the higher-throughput setting removes the risk of hitting an API quota during production. You can enable it before an anticipated event and disable it afterward, or leave it on permanently. For most production workloads, the operational benefit of resilience outweighs the modest cost.&lt;/p&gt;

&lt;p&gt;You can also address latency by optimizing read patterns. For example, you can replace a &lt;code&gt;GetParametersByPath&lt;/code&gt; call with &lt;code&gt;GetParameters&lt;/code&gt;, or cache values in your application. The combination of higher throughput and optimized reads helps make your application more resilient when multiple tasks run concurrently. &lt;/p&gt;

&lt;p&gt;Questions? Comments? Leave them below!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>parameterstore</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>Improving operational resilience by decoupling code deployments from feature activation using AWS AppConfig feature flags</title>
      <dc:creator>Aparna Krishnamoorthy</dc:creator>
      <pubDate>Wed, 13 May 2026 14:31:22 +0000</pubDate>
      <link>https://dev.to/aparkris/improving-operational-resilience-by-decoupling-code-deployments-from-feature-activation-using-aws-i3</link>
      <guid>https://dev.to/aparkris/improving-operational-resilience-by-decoupling-code-deployments-from-feature-activation-using-aws-i3</guid>
      <description>&lt;p&gt;Shipping quickly isn’t the hard part anymore. The hard part is recovering when something goes wrong.&lt;br&gt;
A lot of teams still release features the same way: deploy code and whatever’s in that deployment goes live immediately. It’s simple, and it works — until it doesn’t. Because when a feature causes problems, you don’t just turn off the feature. You end up rolling back the entire deployment. That means waiting, coordinating, and sometimes introducing new risk just to undo the last change.&lt;br&gt;
That’s a heavy price to pay for something that should be lightweight.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Where the risk actually comes from *&lt;/em&gt;&lt;br&gt;
The issue isn’t deployments themselves. It’s the fact that feature activation is tightly coupled to them. When those two things are tied together, every release becomes an all-or-nothing event. A small issue can impact your entire user base, and recovery depends on how quickly you can redeploy or roll back. That’s where Mean Time to Resolution starts to creep up—not because the fix is complicated, but because the mechanism to apply it is. It also makes it difficult to test safely in production. You either release to everyone or no one, which isn’t much of a choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separating deployment from release&lt;/strong&gt;&lt;br&gt;
A more resilient approach is to treat deployment and feature release as two different concerns. You can deploy code whenever you want, but keep new functionality inactive until you explicitly turn it on. That control layer is what feature flags provide.&lt;/p&gt;

&lt;p&gt;Using AWS AppConfig on Amazon Web Services, feature state becomes configuration rather than code. Your application checks that configuration at runtime and decides which path to execute.&lt;br&gt;
That one change removes the need to redeploy just to change behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What changes in practice&lt;/strong&gt;&lt;br&gt;
Instead of hardcoding whether a feature is on or off, your application asks AWS AppConfig for the current state and responds accordingly. The flow is straightforward: the application retrieves configuration, evaluates the relevant flag, and executes the appropriate logic.&lt;br&gt;
Here’s a simple example. Imagine you’re introducing a new checkout flow.&lt;br&gt;
Your configuration might look like this:&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;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"flags"&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;"enableNewCheckout"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enable new checkout experience"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&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;"version"&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;"constraints"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&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;span class="nl"&gt;"values"&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;"enableNewCheckout"&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;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&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;"2.0"&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;That lives in AWS AppConfig, not in your codebase.&lt;/p&gt;

&lt;p&gt;To retrieve it at runtime, use AWS AppConfig Agent. The agent is an Amazon-managed process that retrieves configuration data from AWS AppConfig in the cloud. It caches configuration data locally and asynchronously polls the AWS AppConfig data plane for updates. This approach keeps configuration data readily available to your application while reducing latency and cost.&lt;br&gt;
&lt;em&gt;Although you can retrieve configuration data by calling the APIs directly, using the agent is the recommended approach. It improves application performance and simplifies configuration management.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With your configuration profile stored in AWS AppConfig and the agent running in your compute environment, using the configuration is just a conditional in your application logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;application_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MyDemoApp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MyEnvironment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config_profile_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MyConfigProfile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flag_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// retrieve a single flag's data by providing the "flag" query string parameter&lt;/span&gt;
    &lt;span class="c1"&gt;// note: the configuration's type must be AWS.AppConfig.FeatureFlags&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`http://localhost:2772/applications/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;application_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/environments/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;environment_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/configurations/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config_profile_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?flag=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;flag_name&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCheckout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;enableNewCheckoutFlag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;enableNewCheckout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// {"enabled":true,"version":"2.0"}&lt;/span&gt;


  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enableNewCheckoutFlag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;newCheckoutFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;legacyCheckoutFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;That small shift—moving the decision out of your deployment and into runtime configuration—gives you a surprising amount of control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reducing mean time to resolution with gradual deployments&lt;/strong&gt;&lt;br&gt;
One of the less obvious benefits of feature flags is how they change how quickly you understand and resolve issues, not just how you prevent them.&lt;br&gt;
When you roll out a feature gradually using AWS AppConfig, you’re effectively turning a production release into a controlled experiment. Instead of exposing 100% of users at once, you start with a small segment and watch what happens. That alone shortens the feedback loop. If something is wrong, you’ll see it earlier—and in a much smaller slice of traffic. But the real impact on mean time to resolution comes from the combination of three things working together:&lt;br&gt;
First, reduced scope. If only 5% of users are impacted, you’re not dealing with a full-scale incident. That lowers urgency just enough to make debugging more methodical instead of reactive.&lt;br&gt;
Second, clearer signal. When a change is introduced gradually, it’s easier to correlate cause and effect. You’re not sifting through noise from a full deployment—you’re looking at a controlled change with a defined rollout window.&lt;br&gt;
Third, faster reversal. If metrics start trending in the wrong direction, you don’t need to redeploy or coordinate a rollback. You stop the rollout or revert the configuration.  Integration with Amazon CloudWatch or monitoring platforms like DataDog and New Relic (using custom extensions) allow you to define guardrails. If key metrics cross a threshold, AWS AppConfig can automatically roll back to a known good configuration without waiting for someone to step in.&lt;br&gt;
Put together, gradual deployments don’t just reduce blast radius—they reduce the time it takes to understand, isolate, and resolve issues in the first place.&lt;br&gt;
&lt;em&gt;AWS AppConfig also supports pre-deployment syntax and functional validation through integration with AWS Lambda. Pre-deployment validation reduces the chance of a bad configuration making it into production in the first place.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operational feature flags that make a difference&lt;/strong&gt;&lt;br&gt;
Once you start using feature flags, it’s tempting to think of them only in terms of user-facing features. In practice, some of the most valuable flags are operational—they exist purely to give you more control during incidents or high-risk changes.&lt;br&gt;
A simple but powerful example is an operational toggle or kill switch. Instead of rolling back an entire service, you can disable a specific capability that’s causing issues. This is especially useful for integrations or newly introduced dependencies where failure modes aren’t fully understood yet.&lt;br&gt;
Another common pattern is traffic shaping. You can route a percentage of requests to a new code path, a new backend, or even a different region. If something looks off, you dial it back immediately. This gives you a level of control that traditional deployments don’t offer.&lt;br&gt;
You can also use flags to control fallback behavior. For example, if a downstream dependency starts failing, a flag can switch your application into a degraded mode—serving cached data, skipping non-critical steps, or simplifying responses. That kind of switch can be the difference between a partial degradation and a full outage.&lt;br&gt;
There’s also value in flags that control operational safeguards. Rate limiting thresholds, retry behavior, or timeout values can all be externalized into configuration. Instead of pushing a code change during an incident, you adjust behavior in real time.&lt;br&gt;
Finally, some teams use feature flags to control observability itself. Increasing logging, enabling additional metrics, or turning on debug paths can all be done dynamically. When paired with validation via AWS Lambda, you can safely introduce these changes without risking malformed configurations.&lt;br&gt;
None of these flags are particularly complex on their own. What matters is that they give you options when things aren’t going smoothly.&lt;br&gt;
And that’s really the point: operational resilience isn’t just about preventing failures—it’s about having enough control to respond effectively when they happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When this starts to matter&lt;/strong&gt;&lt;br&gt;
This approach becomes especially valuable once you’re deploying frequently or operating systems where uptime really matters. If you’ve ever had to rush a rollback, or sit through a tense deployment wondering if something might break, you’ve already felt the limitations of tightly coupled releases.&lt;br&gt;
Decoupling feature activation from deployment doesn’t eliminate failures, but it makes them smaller and easier to recover from.&lt;/p&gt;

&lt;p&gt;Most teams focus on improving how they deploy code. Fewer spend time improving how they release features. Using AWS AppConfig shifts that focus. It gives you a way to control behavior independently of deployments, which in turn makes your system more resilient and your releases less stressful. It’s not a massive architectural overhaul. It’s a small change in where decisions are made.&lt;/p&gt;

&lt;p&gt;But it has a disproportionate impact on how safely you can move.&lt;/p&gt;

&lt;p&gt;For more information, see the &lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html" rel="noopener noreferrer"&gt;AWS AppConfig User Guide&lt;/a&gt;.&lt;/p&gt;

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