惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Exploit Database - CXSecurity.com
V
Vulnerabilities – Threatpost
Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
Webroot Blog
Webroot Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
TaoSecurity Blog
TaoSecurity Blog
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
N
News | PayPal Newsroom
S
Security Affairs
T
Tor Project blog
P
Proofpoint News Feed
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Security @ Cisco Blogs
H
Heimdal Security Blog
Hacker News: Ask HN
Hacker News: Ask HN
Help Net Security
Help Net Security
U
Unit 42
云风的 BLOG
云风的 BLOG
The Hacker News
The Hacker News
Cisco Talos Blog
Cisco Talos Blog
量子位
F
Full Disclosure
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
有赞技术团队
有赞技术团队
T
Troy Hunt's Blog
P
Privacy & Cybersecurity Law Blog
Forbes - Security
Forbes - Security
人人都是产品经理
人人都是产品经理
L
Lohrmann on Cybersecurity
Apple Machine Learning Research
Apple Machine Learning Research
Microsoft Security Blog
Microsoft Security Blog
博客园 - Franky
腾讯CDC
AI
AI
Last Week in AI
Last Week in AI
Latest news
Latest news
Google Online Security Blog
Google Online Security Blog
N
Netflix TechBlog - Medium
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
IT之家
IT之家
Martin Fowler
Martin Fowler
Blog — PlanetScale
Blog — PlanetScale
V2EX - 技术
V2EX - 技术
酷 壳 – CoolShell
酷 壳 – CoolShell

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Compile-time feature flags in C# using IL weaving and a Roslyn analyzer
Scott Tippett · 2026-06-24 · via DEV Community

Compile-time feature flags in C# using IL weaving and a Roslyn analyzer

Feature flags are one of those ideas that sound simple until you actually implement them at scale. The concept is clean: gate a feature behind a condition, flip the condition remotely, ship dark. The reality is messier.

After a few years of writing the same pattern everywhere, I decided to do something about it. What I ended up building changed how I think about feature flags entirely: not just the syntax, but the fundamental architecture.


The problem: two things that shouldn't be your problem

1. The if statement noise

Every feature flag in a traditional library adds ceremony at every call site:

if (await _featureManager.IsEnabledAsync("SendWelcomeEmail"))
{
    await SendWelcomeEmailAsync(user);
}

Multiply that across a real codebase and you have if statements in your service layer, your controllers, your background jobs; everywhere. The feature logic and the gating logic are tangled together. When the flag is retired you have to hunt down every call site manually. And the string "SendWelcomeEmail" can drift out of sync with the actual method name silently.

2. No compile-time safety

String keys are the lingua franca of feature flag libraries. "SendWelcomeEmail", "new-checkout-flow", "payment_v2": magic strings that the compiler can't validate, that can be mistyped, that can go stale when you rename a method, that give you no indication at build time that anything is wrong.


Architectural decision 1: the method IS the feature

The first design decision in FtrIO is that [Toggle] should be the only thing you need to add to ship a feature dark, and the only thing you need to remove to retire it.

Instead of wrapping every call site in an if block, you decorate the method itself:

[Toggle]
public void SendWelcomeEmail(User user)
{
    // send the email
}

Then call it normally:

SendWelcomeEmail(user); // only executes if "SendWelcomeEmail": true in appsettings.json

No if. No injected service at the call site. No magic string. The method name is the toggle key.

When you are ready to retire the flag, you remove [Toggle]. That is the entire change. There are no call sites to update because there were never any call sites wrapping the feature. The feature logic and the gating logic were never tangled together. The method goes back to being a normal method.

This is the design goal: a feature toggle should be a single attribute on a single method. Everything else should be unchanged.

For async methods:

[ToggleAsync]
public async Task SendWelcomeEmailAsync(User user)
{
    await _emailClient.SendAsync(user.Email);
}

await SendWelcomeEmailAsync(user); // safely awaitable whether on or off

When the toggle is off, [ToggleAsync] returns Task.CompletedTask; always safely awaitable, no null reference risk.


Architectural decision 2: radical ownership

The second design decision is about where your flag data lives.

FtrIO is built from the ground up around you owning your flag data. Not as a fallback, not as an offline mode, not as an escape hatch: as the primary design. Your flag state lives in appsettings.json, alongside your application code, on your server, in your repository. It is a first-class citizen of your app, not a remote dependency.

If you use a remote provider, it runs in the background, polls for changes, and writes the updated state into your local file via an atomic buffer. The read path always reads from your file.

Remote provider         ToggleProviderBuffer        appsettings.json
(HTTP / Azure /    →    (stages changes,        →   (source of truth,
 env vars)              flushes atomically)          always yours)
                                                          ↓
                                                     [Toggle] method
                                                     (reads at call time)

The consequences of this are significant:

Your flag state is inspectable. Open appsettings.json and you can see exactly what every toggle is set to. No dashboard login, no API call, no SDK required. It's a text file sitting next to your code.

Your flag state is portable. It's JSON in a file you control. Copy it, back it up, diff it in git, deploy it with your app. It moves with your codebase because it lives in your codebase.

Your flag state is editable directly. In an emergency, edit appsettings.json and the change is picked up via ReloadOnChange with no restart. No external access required.

Fresh start resilience is free. On a cold start FtrIO reads whatever is in appsettings.json immediately. No connection to establish, no initialisation to wait for.

Remote providers are optional, not foundational. Use HTTP, Azure App Config, or env vars if you want remote control. Remove them and appsettings.json is still your fully functional flag store. The call sites don't change either way.

This is what radical ownership means in practice. The data lives with your app. You control it completely.


How IL weaving makes decision 1 possible

[Toggle] is not magic. It is an aspect powered by AspectInjector, a compile-time IL weaving library.

When you build your project, AspectInjector walks the compiled IL, finds every method decorated with [Toggle], and weaves a gate check directly into the method's IL, before the method body executes. The gate reads the toggle state from ToggleParser, which reads from appsettings.json. If the toggle is off, the method returns immediately. If it is on, the method body executes as normal.

The weaving happens at compile time. At runtime there is no reflection, no proxy, no dynamic invocation; just a direct IL call. The method behaves identically to a method that was always written with the gate logic inline, except you didn't have to write it.

This is why removing the attribute is a clean operation. There is no call site to update. The aspect is gone from the IL, the gate is gone, and the method is back to normal behaviour.


Roslyn closes the loop

The IL weaving solves the syntax problem. But there is still the question of what happens if you decorate a method with [Toggle] and forget to add the corresponding entry to appsettings.json.

Without any additional tooling, this is a silent runtime failure; the toggle evaluates as missing and the feature quietly does not run.

FtrIO ships a Roslyn analyzer, FTRIO001, that catches this at build time.

Add appsettings.json as an AdditionalFile in your .csproj:

<ItemGroup>
  <AdditionalFiles Include="appsettings.json" />
</ItemGroup>

Now if you write this:

[Toggle]
public void NewCheckoutFlow() { ... }

Without adding "NewCheckoutFlow" to your Toggles section in appsettings.json, the build fails:

error FTRIO001: 'NewCheckoutFlow' is decorated with [Toggle]
but has no corresponding entry in the Toggles config section.

This closes the last gap in the safety net. The compiler validates the contract between your code and your config. Magic string drift is caught before it ever reaches a running application.

Rider surfaces FTRIO001 as a standard compiler error in the editor; you see it inline as you type, in the Problems view, and in the build output. No plugin required.


Strategy-based decisions beyond true/false

Honestly? This was an afterthought.

When I first started this project years ago, I only ever expected features to run or not run. On or off. That was the whole mental model. I put it down for a long time, picked it up again with fresh eyes, and realised the world is a bit more nuanced than that. Percentage rollouts. Blue-green deployments. A/B tests. Per-user targeting. These are real things real teams need.

So I added them. But I want to be clear about what they are: add-ons that all ultimately boil down to the same thing. Should this method execute or not? The strategies are just different ways of answering that question. The call site never changes.

I'm genuinely excited about where this goes next. The foundation is solid and there's a lot of interesting territory still to explore. But the core idea — one attribute, run or not run — is still what drives everything.

Not every feature flag is boolean. FtrIO's StrategyToggleParser routes raw config values through a chain of IToggleDecisionStrategy implementations, with BooleanStrategy always appended as the final fallback.

{
  "Toggles": {
    "NewCheckoutFlow":   "20%",
    "PaymentV2":         "blue",
    "BetaFeature":       "users:alice,bob",
    "PremiumDashboard":  "attribute:plan equals premium",
    "Experiment":        "ab:50"
  }
}

ToggleParserProvider.Configure(new StrategyToggleParser(
    new UserTargetingStrategy(accessor),
    new AttributeRuleStrategy(accessor),
    new ABTestStrategy(accessor),
    new PercentageRolloutStrategy(),
    new BlueGreenStrategy("blue", "blue", "green")
));

Each strategy implements CanHandle(string rawValue) and ShouldExecute(string key, string rawValue). The parser tries each strategy in order; first match wins, BooleanStrategy catches everything else.

PercentageRolloutStrategy handles "20%": probabilistic, runs approximately 1 in 5 calls.

BlueGreenStrategy handles "blue" / "green": compares the config value to the current deployment slot.

UserTargetingStrategy handles "users:alice,bob": checks the current user ID against a comma-separated allow list via IFtrIOContextAccessor.

AttributeRuleStrategy handles "attribute:plan equals premium": evaluates an attribute rule against the current user context. Supports equals, notEquals, startsWith, endsWith, contains, in, notIn.

ABTestStrategy handles "ab:50": deterministic bucketing via SHA-256 of the user ID and toggle key, mod 100. The same user always gets the same result for a given toggle. Salt support ("ab:50:round2") allows population reassignment without changing the toggle key.

All of this is driven by the value in appsettings.json. The call site is always the same:

[Toggle]
public void NewCheckoutFlow() { ... }

The strategy chain is wired up once at startup. The config value determines which strategy fires. The [Toggle] attribute knows nothing about strategies.


The full picture

FtrIO is not just a library. It is an ecosystem built around the two architectural decisions above.

FtrIO.Toaster is a self-hosted Docker web UI for managing toggles live without editing files. It implements FtrIO's own ToggleProviderBuffer internally; changes are staged and flushed atomically to appsettings.json, picked up via ReloadOnChange with no restart.

FtrIO.onetwo is a .NET CLI audit tool. It scans your source tree for every [Toggle] reference, cross-references against appsettings.json, and reports the state of each toggle: ON, OFF, 20%, BLUE, AB-TEST(50%), TARGETED(alice,bob), MISSING, with file and line number. It also has experimental subcommands for importing flag state from LaunchDarkly, Flagsmith, and Microsoft.FeatureManagement, generating migration reports, and ejecting back out to another system with a structured report.

export-manifest-action and release-check-action are GitHub Actions that together gate deployments on missing toggle config. The export action scans the source tree and uploads a manifest of required toggle keys as a build artifact. The release-check action downloads the manifest and validates it against the target environment's config before deploying, blocking the deploy if any keys are missing.

The full safety net:

Stage What catches it
Write [Toggle] without config Roslyn analyzer: compile time
Deploy with key missing from target env release-check-action: deploy time

Getting started

dotnet add package FtrIO

<PackageReference Include="AspectInjector" Version="2.9.0" />

{
  "FtrIO": { "ReloadOnChange": true },
  "Toggles": {
    "SendWelcomeEmail": true,
    "NewCheckoutFlow": false
  }
}

[Toggle]
public void SendWelcomeEmail() { ... }

SendWelcomeEmail(); // runs only if "SendWelcomeEmail": true

Everything else is opt-in. Toaster when you want a UI. onetwo when you want audit tooling. Strategies when you need targeting. GitHub Actions when you want deployment safety. You never need all of it.


Links