<?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: kododo</title>
    <description>The latest articles on DEV Community by kododo (@kododo).</description>
    <link>https://dev.to/kododo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3918541%2Fdd2c7bde-9957-4871-8954-ab62696b4948.png</url>
      <title>DEV Community: kododo</title>
      <link>https://dev.to/kododo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kododo"/>
    <language>en</language>
    <item>
      <title>I built ConfigWay — a runtime config editor for ASP.NET Core (my first open source project)</title>
      <dc:creator>kododo</dc:creator>
      <pubDate>Thu, 07 May 2026 19:47:40 +0000</pubDate>
      <link>https://dev.to/kododo/i-built-configway-a-runtime-config-editor-for-aspnet-core-my-first-open-source-project-2jok</link>
      <guid>https://dev.to/kododo/i-built-configway-a-runtime-config-editor-for-aspnet-core-my-first-open-source-project-2jok</guid>
      <description>&lt;p&gt;You know that moment when you tweak a feature flag or an email template prefix in &lt;code&gt;appsettings.json&lt;/code&gt;, rebuild, redeploy, wait… and then tweak it again? I got tired of that loop. So I built something to fix it — and decided to open source it.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;ConfigWay&lt;/strong&gt; — a runtime configuration editor for ASP.NET Core. It lets you view and modify your &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt; values through a built-in web UI, without ever restarting the application.&lt;/p&gt;

&lt;p&gt;This is my first open source project, and I'm genuinely excited to share it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;ASP.NET Core's configuration system is great, but once the app is running, changing a value means editing a file and restarting. For settings that need frequent tuning — timeouts, feature flags, email templates, rate limits — that cycle adds up fast.&lt;/p&gt;

&lt;p&gt;I wanted something that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;works with the existing &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt; pattern I already use&lt;/li&gt;
&lt;li&gt;requires minimal setup&lt;/li&gt;
&lt;li&gt;stores overrides in a real database so they survive restarts&lt;/li&gt;
&lt;li&gt;gives me a clean UI to make changes from a browser&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What ConfigWay does
&lt;/h2&gt;

&lt;p&gt;ConfigWay adds a small web UI to your application. Any &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt; class you register becomes an editable form — with type-aware controls (toggle for &lt;code&gt;bool&lt;/code&gt;, dropdown for &lt;code&gt;enum&lt;/code&gt;, array editor for collections, etc.).&lt;/p&gt;

&lt;p&gt;Changes are applied immediately via hot-reload. No restart needed.&lt;/p&gt;

&lt;p&gt;Here's a quick demo: &lt;strong&gt;&lt;a href="https://kododo.dev/configway/demo" rel="noopener noreferrer"&gt;kododo.dev/configway/demo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting started in 3 steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install the packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Kododo.ConfigWay
dotnet add package Kododo.ConfigWay.UI
dotnet add package Kododo.ConfigWay.PostgreSQL  &lt;span class="c"&gt;# optional — for persistence&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Register everything
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddConfigWay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FeatureFlagOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUiEditor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UsePostgreSql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DefaultConnection"&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;h3&gt;
  
  
  3. Mount the UI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseConfigWay&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// mounts the editor at /config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Open &lt;code&gt;/config&lt;/code&gt; in your browser and you'll see all your registered options, editable in real time.&lt;/p&gt;




&lt;h2&gt;
  
  
  A few things I'm particularly happy with
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It plays nicely with validation.&lt;/strong&gt; If you already have &lt;code&gt;ValidateDataAnnotations()&lt;/code&gt; or a custom &lt;code&gt;IValidateOptions&amp;lt;T&amp;gt;&lt;/code&gt; wired up, ConfigWay will show validation errors in the UI and block saving until they're resolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sensitive fields stay hidden.&lt;/strong&gt; Mark any &lt;code&gt;string&lt;/code&gt; with &lt;code&gt;[DataType(DataType.Password)]&lt;/code&gt; and ConfigWay treats it as a secret — rendered as &lt;code&gt;●●●●●&lt;/code&gt;, never returned from the API, requiring an explicit reset to remove.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reset to default.&lt;/strong&gt; Every field has a ↩ button that appears when the stored value differs from the underlying config layer (appsettings.json, environment variable). One click removes the override and the original value takes effect — no restart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizable labels.&lt;/strong&gt; Use &lt;code&gt;[Display(Name = "...", Description = "...")]&lt;/code&gt; to control how fields and sections appear in the UI. The &lt;code&gt;Description&lt;/code&gt; shows up as a tooltip icon next to the label.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture in a nutshell
&lt;/h2&gt;

&lt;p&gt;The library is split into focused packages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Kododo.ConfigWay.Core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Abstractions (&lt;code&gt;IStore&lt;/code&gt;, &lt;code&gt;Setting&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Kododo.ConfigWay&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DI registration, in-memory store, hot-reload logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Kododo.ConfigWay.UI&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Embedded React SPA served from the host app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Kododo.ConfigWay.PostgreSQL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL persistence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The UI is embedded as a resource inside the DLL — no CDN, no separate static file deployment. The PostgreSQL store creates a single &lt;code&gt;configway.settings&lt;/code&gt; table on first startup and handles everything from there.&lt;/p&gt;

&lt;p&gt;You can also plug in your own backend by implementing the &lt;code&gt;IStore&lt;/code&gt; interface from &lt;code&gt;Kododo.ConfigWay.Core&lt;/code&gt;:&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="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddConfigWay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MyRedisStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  This is just the beginning
&lt;/h2&gt;

&lt;p&gt;ConfigWay is my first open source project, and shipping it has been equal parts exciting and nerve-wracking. There's a lot I want to add — more storage providers, role-based field access, change history — but I wanted to get something real and useful out first.&lt;/p&gt;

&lt;p&gt;I'm also working on more projects in the &lt;strong&gt;Kododo&lt;/strong&gt; family. If this kind of thing is useful to you, I'd love it if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;&lt;a href="https://github.com/kododo-dev/ConfigWay" rel="noopener noreferrer"&gt;Starred the repo&lt;/a&gt;&lt;/strong&gt; — it genuinely helps with visibility&lt;/li&gt;
&lt;li&gt;👀 &lt;strong&gt;&lt;a href="https://github.com/kododo-dev" rel="noopener noreferrer"&gt;Followed me on GitHub&lt;/a&gt;&lt;/strong&gt; — more libraries are coming&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Opened an issue or a discussion&lt;/strong&gt; — feedback from real users shapes the roadmap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading. I hope ConfigWay saves you a few restarts. 🚀&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>opensource</category>
      <category>aspnetcore</category>
    </item>
  </channel>
</rss>
