<?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: David Gates</title>
    <description>The latest articles on DEV Community by David Gates (@dgates82).</description>
    <link>https://dev.to/dgates82</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%2F4030586%2F2d421068-422a-476b-8675-f7b3b4882494.jpg</url>
      <title>DEV Community: David Gates</title>
      <link>https://dev.to/dgates82</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dgates82"/>
    <language>en</language>
    <item>
      <title>Modern Secrets for Legacy .NET: Building a Typed, Cached AWS Secrets Manager Library</title>
      <dc:creator>David Gates</dc:creator>
      <pubDate>Thu, 16 Jul 2026 10:53:27 +0000</pubDate>
      <link>https://dev.to/dgates82/modern-secrets-for-legacy-net-building-a-typed-cached-aws-secrets-manager-library-2mpp</link>
      <guid>https://dev.to/dgates82/modern-secrets-for-legacy-net-building-a-typed-cached-aws-secrets-manager-library-2mpp</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;If you're building on modern .NET, retrieving a secret from AWS Secrets Manager as a strongly typed, cached configuration value is pretty much turnkey. AWS's supporting NuGet packages handle TTL-based (time-to-live) caching out of the box, and let you bind secrets straight into &lt;code&gt;IConfiguration&lt;/code&gt; with only a few lines of code.&lt;/p&gt;

&lt;p&gt;If you're maintaining a .NET Framework 4.8 application — and there are more of these in production than any of us would like to admit — none of that tooling exists. The AWS SDK for .NET supports .NET Framework 4.8; you can certainly call &lt;code&gt;AmazonSecretsManagerClient.GetSecretValueAsync&lt;/code&gt; from a Framework app today. What doesn't exist is the supporting ecosystem that modern .NET developers take for granted: no typed binding, no caching client, and nothing comparable to &lt;code&gt;IConfiguration&lt;/code&gt;'s abstraction over configuration sources. You're left hand-rolling JSON deserialization, deciding for yourself whether and how to cache secrets, and wiring the rest together by hand.&lt;/p&gt;

&lt;p&gt;A lot of what's still running out there is classic ASP.NET MVC 5 — &lt;code&gt;System.Web.Mvc&lt;/code&gt;, &lt;code&gt;Global.asax&lt;/code&gt;-based startup, with no built-in dependency injection (DI). That's not a niche scenario; it's the reality for many enterprise applications across organizations of every size.&lt;/p&gt;

&lt;p&gt;I ran into this gap firsthand when my organization decided to standardize on AWS Secrets Manager across both modern and legacy .NET applications. The modern apps were straightforward: install the packages, bind to &lt;code&gt;IConfiguration&lt;/code&gt;, and you're done. The first .NET Framework 4.8 application was a different story. It needed the same typed retrieval, caching, and clean integration, but none of that infrastructure existed. Every legacy application in our portfolio would face the same challenge. I built &lt;code&gt;DGates.AwsSecretsManager&lt;/code&gt; to solve it once, rather than repeating the same solution across every legacy application.&lt;/p&gt;

&lt;p&gt;So the goal here wasn't to invent AWS Secrets Manager support for .NET Framework; that already existed. The goal was to close the &lt;em&gt;ergonomics&lt;/em&gt; gap: build the kind of typed, cached, DI-friendly wrapper that modern .NET developers get for free today, in a form that a legacy .NET Framework app — including one with no DI container and no modern configuration pipeline — can actually adopt without a rewrite.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/dgates82/DGates.AwsSecretsManager" rel="noopener noreferrer"&gt;source&lt;/a&gt;, &lt;a href="https://www.nuget.org/packages/DGates.AwsSecretsManager" rel="noopener noreferrer"&gt;NuGet package&lt;/a&gt;, and &lt;a href="https://github.com/dgates82/DGates.AwsSecretsManager.Examples" rel="noopener noreferrer"&gt;example applications&lt;/a&gt; are all available if you'd like to follow along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Decisions &amp;amp; Trade-offs
&lt;/h2&gt;

&lt;p&gt;This is the part of the library that matters — not "I called an AWS SDK method," but the decisions around &lt;em&gt;how&lt;/em&gt; to expose it. Every decision came back to one constraint: the library had to feel natural in a .NET Framework 4.8 application, without requiring that application to become more "modern" to use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typed retrieval, not string wrangling.&lt;/strong&gt; The library's API centers on &lt;code&gt;GetSecretAsync&amp;lt;T&amp;gt;&lt;/code&gt; — deliberately mirroring the typed configuration binding modern .NET developers already expect. Tell it what shape you expect back, and it hands you an object — not a JSON blob you deserialize yourself three call sites later. &lt;code&gt;GetSecretStringAsync&lt;/code&gt; exists too, for the cases where a secret genuinely is just a string and wrapping it in a POCO (plain old CLR object) would be overkill. &lt;code&gt;RefreshSecretAsync&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;InvalidateCache&lt;/code&gt; round out the API — force a refresh when you know a secret rotated, or drop it from cache entirely without waiting for TTL expiry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching: &lt;code&gt;ConcurrentDictionary&lt;/code&gt;, not &lt;code&gt;IMemoryCache&lt;/code&gt;.&lt;/strong&gt; This one is a deliberate departure from what a lot of people would reach for first. &lt;code&gt;Microsoft.Extensions.Caching.Memory&lt;/code&gt; is the obvious modern choice, but pulling it into a .NET Framework 4.8 library means dragging in a dependency chain built for a different runtime generation, plus the DI registration ceremony that comes with it. That's exactly the kind of friction we want to avoid with this library. A &lt;code&gt;ConcurrentDictionary&lt;/code&gt; keyed by secret name, storing the value alongside its expiry, does everything needed here: thread-safe reads and writes, no extra package, no DI requirement. &lt;code&gt;CacheTtl&lt;/code&gt; is exposed as a &lt;code&gt;TimeSpan&lt;/code&gt; rather than an int, because "how long should this live" is a duration, and forcing a caller to remember whether an int means seconds or milliseconds is the kind of API ambiguity that causes production incidents at 2 a.m.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resilience: Polly, scoped narrowly.&lt;/strong&gt; Secrets Manager calls can fail transiently, and retrying blindly can be worse than not retrying at all if you're not careful about &lt;em&gt;what&lt;/em&gt; you retry. Polly 8 wraps the AWS calls specifically for transient failure modes — throttling, temporary network failures, and similar conditions — not as a blanket "wrap everything and hope" policy. Non-transient failures, like invalid credentials or a secret that does not exist, surface immediately instead of being retried into a longer, more confusing failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DI-agnostic by design.&lt;/strong&gt; This decision came directly from the target audience. A modern ASP.NET app has a DI container built in. A classic ASP.NET MVC 5 app, running through &lt;code&gt;Global.asax&lt;/code&gt;, does not. Forcing a specific container (Autofac, Unity, whatever) onto a legacy app just to use this library would be limiting and defeat the purpose. Instead, &lt;code&gt;SecretsManagerServiceFactory&lt;/code&gt; builds a configured &lt;code&gt;SecretsManagerService&lt;/code&gt; directly, and &lt;code&gt;SecretsManagerService&lt;/code&gt; itself is directly instantiable — no container required. Consumers who &lt;em&gt;do&lt;/em&gt; have a DI container can register it as usual; consumers who don't can call the factory once at startup and hold the result in a static accessor. Both are first-class, not one blessed path and one workaround.&lt;/p&gt;

&lt;p&gt;The same restraint applies to authentication. &lt;code&gt;SecretsManagerSettings&lt;/code&gt; accepts an explicit &lt;code&gt;AccessKey&lt;/code&gt; and &lt;code&gt;SecretKey&lt;/code&gt; for the cases that genuinely need them, but when those values are omitted, the library defers to the AWS SDK's standard credential chain — environment variables, an &lt;code&gt;.aws/credentials&lt;/code&gt; profile, or an IAM role. Rather than inventing its own authentication model, the library relies on the one AWS developers already know and expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local development without AWS.&lt;/strong&gt; None of the above matters much if testing it locally means burning AWS API calls against a real Secrets Manager instance every time you hit F5, or requiring an AWS account just to try the library out. The library ships with a &lt;code&gt;docker-compose.yml&lt;/code&gt; that spins up LocalStack and a seed script that populates it with test secrets, so a fresh checkout can run the integration tests against a fully local AWS emulation with a single command: no AWS account, no manual setup. The examples repo follows the same pattern.&lt;/p&gt;

&lt;p&gt;For environments where Docker isn't available — nested VMs without virtualization passthrough are a common setup in practice — there's a &lt;code&gt;LocalJsonFallbackPath&lt;/code&gt; that reads secrets from a local JSON file instead. It's not a mock; it's a genuine fallback path, wired the same way the real client is, so switching between "talk to LocalStack," "talk to real AWS," and "read a local file" is a configuration change, not a code change. (To be clear: this fallback is a local development and testing convenience, not something intended for production use.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving It in a Real App: MvcExample
&lt;/h2&gt;

&lt;p&gt;Could every application implement this itself? Absolutely — the AWS SDK makes that possible, if not particularly pleasant. But once you're standardizing Secrets Manager across dozens of legacy applications, the problem isn't writing another JSON deserializer. It's making sure every application gets the same caching behavior, retry policy, configuration model, and local development experience — instead of a dozen slightly different, independently maintained versions of the same idea.&lt;/p&gt;

&lt;p&gt;A library intended for ASP.NET MVC 5 isn't really proven by a console sample alone. To validate &lt;code&gt;DGates.AwsSecretsManager&lt;/code&gt; in the kind of environment it was actually built for, I created a classic ASP.NET MVC 5 application — &lt;a href="https://github.com/dgates82/DGates.AwsSecretsManager.Examples/tree/main/src/MvcExample" rel="noopener noreferrer"&gt;MvcExample&lt;/a&gt; — where a user enters a city, the app retrieves an OpenWeatherMap API key from Secrets Manager, geocodes the city, and fetches live weather. A "how this worked" panel shows the secret name, the retrieved URL, and which backend served the request — real AWS, LocalStack, or the JSON fallback.&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%2F89diyqr6q6tubdy1e5kw.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%2F89diyqr6q6tubdy1e5kw.png" alt="MvcExample showing weather results for a searched city, with the " width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Startup integration is a single factory call — no DI container required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Global.asax.cs — Application_Start&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SecretsManagerServiceFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SecretsManagerSettings&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"us-west-2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CacheTtl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;LocalJsonFallbackPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"~/App_Data/secrets.local.json"&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;Retrieval is one typed call — the secret comes back as a POCO, already deserialized, already cached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetSecretAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OpenWeatherMapSecret&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"dev/MvcExample/OpenWeatherMap"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the ergonomics gap from the start of this post, closed: typed, cached, and wired into a &lt;code&gt;Global.asax&lt;/code&gt;-era app without a rewrite.&lt;/p&gt;

&lt;p&gt;In MvcExample, both calls live behind a small static accessor class: it reads the settings from &lt;code&gt;Web.config&lt;/code&gt; instead of hardcoding them, guards initialization so the service is built exactly once, and wraps retrieval results with the metadata the "how this worked" panel displays. None of that is required by the library itself — it's the app's own glue — but the pattern is worth copying if you want environment-specific configuration without a recompile. See &lt;a href="https://github.com/dgates82/DGates.AwsSecretsManager.Examples/blob/main/src/MvcExample.Infrastructure/SecretsManagerAccessor.cs" rel="noopener noreferrer"&gt;SecretsManagerAccessor.cs&lt;/a&gt; for the full version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legacy packaging friction.&lt;/strong&gt; MVC5 projects use &lt;code&gt;packages.config&lt;/code&gt;, and &lt;code&gt;DGates.AwsSecretsManager&lt;/code&gt; brings the AWS SDK's sizeable transitive dependency tree with it — something you do not want to manage through hand-wired &lt;code&gt;HintPath&lt;/code&gt; entries. The long-term solution is to convert the legacy project's packages to &lt;code&gt;PackageReference&lt;/code&gt; and isolate all library-facing code in a small SDK-style project — in this case, &lt;code&gt;MvcExample.Infrastructure&lt;/code&gt;. The web project references that project through &lt;code&gt;ProjectReference&lt;/code&gt;, letting MSBuild resolve the full dependency graph while the legacy project never has to know the AWS SDK exists. As a bonus, the conversion also enables &lt;code&gt;dotnet restore&lt;/code&gt;, making clean command-line and CI builds possible without relying on Visual Studio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two operational lessons.&lt;/strong&gt; First, &lt;code&gt;Application_Start&lt;/code&gt; runs eagerly — so an unreachable Secrets Manager at startup meant a crashed app pool, not an error page. Initialization is now handled defensively, surfacing failures as an in-page message at first real use instead. Second, &lt;code&gt;LocalJsonFallbackPath&lt;/code&gt; isn't automatic — if you want the fallback, it has to be wired explicitly at the consumer's configuration call site. It was missing entirely from MvcExample's initial setup, a reminder that it needs to be verified in every new consumer, not assumed from the README.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the panel doesn't show.&lt;/strong&gt; The "how this worked" panel doesn't display a cache-hit indicator, and that's deliberate. Building one would have meant inferring the library's internal cache state from the outside rather than reporting something real — false precision dressed up as telemetry. Better to show nothing than something that looks like data but isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building &amp;amp; Publishing
&lt;/h2&gt;

&lt;p&gt;A .NET Framework 4.8 library in 2026 raises an immediate CI question: what does the pipeline even run on? GitHub Actions' Windows runners are the obvious answer, but LocalStack ships as a Linux container and isn't supported under Windows containers. The &lt;a href="https://github.com/dgates82/DGates.AwsSecretsManager" rel="noopener noreferrer"&gt;library&lt;/a&gt; is built and tested on &lt;code&gt;ubuntu-latest&lt;/code&gt; instead, using Mono to run the .NET Framework test suite. Linux runners also spin up faster, and keeping the pipeline on one OS keeps it simple — the same environment builds the library, runs the tests against LocalStack, and packs the NuGet package.&lt;/p&gt;

&lt;p&gt;There's one hard limit to know about: this works for &lt;em&gt;building&lt;/em&gt; .NET Framework code, not running classic ASP.NET on Linux. Compiling the MVC 5 example app on &lt;code&gt;ubuntu-latest&lt;/code&gt; works fine; hosting it does not — Mono's &lt;code&gt;xsp4&lt;/code&gt; web host is broken and unmaintained. Build on Linux, run on Windows via IIS Express. That boundary is worth knowing before you design a pipeline around either assumption.&lt;/p&gt;

&lt;p&gt;Two principles shaped the rest of the pipeline:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No NuGet API keys, anywhere.&lt;/strong&gt; Publishing uses NuGet's Trusted Publishing — the workflow authenticates via OIDC (OpenID Connect), meaning NuGet.org trusts a short-lived token issued by GitHub Actions for this specific repository and workflow, rather than a long-lived API key stored as a repository secret. There is nothing to leak, rotate, or accidentally commit. For a library whose entire purpose is keeping secrets out of the wrong places, publishing it with a static credential would have been a little too ironic.&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%2Fuqwn5kromw4hm0jnbowm.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%2Fuqwn5kromw4hm0jnbowm.png" alt="DGates.AwsSecretsManager listed on NuGet.org, showing the prerelease version, install command, and download stats" width="799" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration tests run against real (local) infrastructure.&lt;/strong&gt; The same &lt;code&gt;docker-compose.yml&lt;/code&gt; and seed script that power local development spin up LocalStack in CI, so the integration tests exercise genuine Secrets Manager API behavior on every push — not mocks. The local-dev investment from earlier paid for itself here: CI didn't need its own separate test infrastructure, because the local story already was one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Three things stand out looking back at this project — none of them about AWS or Secrets Manager specifically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local development isn't an afterthought.&lt;/strong&gt; The LocalStack setup and the JSON fallback path took real time to build — time that could have gone toward features. It was worth it twice over. During development, the zero-dependency local story meant every design decision could be exercised immediately, in environments ranging from a full Docker setup to a locked-down VM with nothing on it. Then it paid off again in CI: the same &lt;code&gt;docker-compose.yml&lt;/code&gt; and seed script that power local development became the integration test infrastructure, with no separate CI-only setup to build or maintain. Investment in the development experience compounds; it isn't overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for the audience you actually have.&lt;/strong&gt; It would have been easier to build this with the newest patterns available: a modern DI container, &lt;code&gt;IOptions&lt;/code&gt;-style configuration, the latest caching abstractions. The applications that need this library can't use any of that. Every design decision in this post traces back to accepting that constraint instead of fighting it: the DI-agnostic factory exists because the target apps have no container, converting package management is a much smaller step than rewriting the project around an SDK-style project model, and configuration flows through &lt;code&gt;Web.config&lt;/code&gt; because that's where these apps keep it. Meeting legacy applications where they are is what makes incremental modernization possible at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistency scales; convenience doesn't.&lt;/strong&gt; Any individual application could hand-roll its own Secrets Manager integration in an afternoon. Taken once, that's perfectly reasonable. Taken a dozen times across a portfolio, it produces a dozen slightly different caching policies, retry behaviors, and configuration conventions, each independently maintained, each evolving in slightly different directions. A shared library means one implementation to test, one set of behaviors to reason about, and one place to fix a bug that would otherwise be a dozen tickets. The value isn't that it saved anyone from writing a JSON deserializer — it's that nobody has to wonder which version of the pattern any given app is running.&lt;/p&gt;

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

&lt;p&gt;The surprising part of this project wasn't the implementation itself. It was realizing that the hardest problems weren't making it work — they were making it fit. Legacy applications aren't difficult because they're old; they're difficult because they're valuable enough to keep running while the world around them changes.&lt;/p&gt;

&lt;p&gt;That's ultimately what &lt;code&gt;DGates.AwsSecretsManager&lt;/code&gt; aims to provide: not new capabilities, but modern ergonomics for software that's still doing real work.&lt;/p&gt;

&lt;p&gt;If this library helps even a handful of teams modernize legacy applications incrementally instead of rewriting them all at once, it will have done exactly what it was built to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The library is currently in beta. Before a stable 1.0 release, I want to focus on two areas: structured logging and deeper documentation of the design decisions covered here. Beyond that are ideas like cache stampede protection, serializer abstraction, and rotation handling — but roadmaps change, so the &lt;a href="https://github.com/dgates82/DGates.AwsSecretsManager/issues" rel="noopener noreferrer"&gt;issue tracker&lt;/a&gt; remains the source of truth for what's actually planned.&lt;/p&gt;

&lt;p&gt;If you're maintaining .NET Framework applications and this problem looks familiar, I'd love to hear how you're solving it — or whether &lt;a href="https://www.nuget.org/packages/DGates.AwsSecretsManager" rel="noopener noreferrer"&gt;&lt;code&gt;DGates.AwsSecretsManager&lt;/code&gt;&lt;/a&gt; helps. Feedback from real legacy codebases is worth more than any roadmap.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aws</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
