Status: Accepted · Date: 2026-07-30 Supersedes: none · Superseded by: none
The first compilation of FlowX.Abstractions produced 29 analyzer errors. Fourteen
of them were CA1716 — "Identifiers should not match keywords", firing on:
| Identifier | Where | Conflicts with |
|---|---|---|
Step |
IFlowBuilder.Step<TCapability>() |
VB Step |
Return |
IFlowBuilder.Return(...) |
VB Return |
When |
IFlowBuilder.When(...) |
VB When |
then |
parameter of When |
VB Then |
Error |
the Error record, FlowContext.Error, parameter of Fail |
VB Error |
Get / Set |
FlowContext.Get<T>() / Set<T>() |
VB Get / Set |
CA1716 exists so that a type authored in one .NET language can be consumed comfortably from another — principally VB.NET, where these are statement keywords and a consumer would need escaping syntax.
The list is not a coincidence. It is, almost exactly, the FlowX DSL's entire vocabulary. These names appear in every sample, in all twenty specification documents, and in the one code block on the README that has to make the platform make sense in ten seconds:
flow.Step<ValidateOrder>()
.Step<ReserveInventory>().CompensateWith<ReleaseInventory>()
.When(ctx => ctx.Input.IsExpress, then => then.Step<PriorityRouting>())
.Return(ctx => new OrderPlacedResult(ctx.Get<OrderId>()));Disable CA1716 repository-wide, in .editorconfig, with a comment pointing
here.
The rule and this codebase optimise for different things, and the conflict is not resolvable by compromise.
- The audience the rule protects does not exist here. FlowX is C#-first by constraint C1. No VB.NET or F# consumer story is designed, tested, sampled or documented. Paying a permanent readability cost for a hypothetical consumer is the wrong trade.
- The alternative names are all worse.
ExecuteStep,ReturnValue,WhenCondition,GetValue— each is longer, less precise, and reads as though it were named by a linter rather than by a designer. A fluent DSL's whole value is that it reads like the domain;flow.ExecuteStep<T>()does not. - The names are already public contract. They appear across the whole specification set. Renaming them here would make the code and its documentation disagree, which constraint C8 forbids and which is a far more expensive defect than a cross-language ergonomics warning.
- Suppressing narrowly would be worse than suppressing globally. Fourteen
inline
[SuppressMessage]attributes on the most-read type in the codebase would be visual noise on exactly the surface that must be pristine. One documented decision beats fourteen scattered apologies.
Accepted:
- A VB.NET consumer of
FlowX.Abstractionsmust use bracket escaping ([Step],[Error]). This is a documented, accepted limitation. - The repository loses CA1716's protection everywhere, not just on the DSL. The risk is small: the rule's remaining value is for identifiers nobody would choose accidentally.
Gained:
- The DSL reads as designed, in code and in documentation, identically.
Revisit when: a first-class VB.NET or F# consumer story is adopted as a goal — at which point this ADR is superseded and the rule is re-enabled with per-member suppressions on the DSL surface only.
This clause was headed "Reversal condition" until 2026-07-31, which is why
the ADR index's own rule — "an ADR with no 'Revisit when' is a decision nobody
can ever safely change" — read this record as having none. It was renamed rather than
supplemented: the condition already stated is exactly what a Revisit when is for, and
adding a second clause beside it would have given one decision two triggers that could
disagree. Nothing about the condition changed, and the ADR index summarises the same one it
always did. The Consequences above are headed Accepted and Gained rather than Negative
and Positive; that is the same labelling drift in the other direction and it is left alone,
because the section the rule asks for is present and argued — the rule is about a record
that has not been thought through, not about a heading.
Per 21-Quality-Gates §6.3, a
documented trade-off recorded in an ADR is a decision, not debt. Nothing here
would be "fixed given time" — given infinite time, the same choice is made again.
It therefore carries no DEBT-#### id and no expiry date, and it does not count
against the debt budget.
The distinction matters: if decisions were logged as debt, the register would fill with things nobody intends to change, and the budget that makes the register enforceable would become meaningless.
- The other 15 errors from the same build were IDE0040 on interface members,
caused by this repository's own
.editorconfigsettingdotnet_style_require_accessibility_modifiers = always. Interface members are implicitly public and no C# codebase writes the modifier out. That was a configuration defect, not a design question, and was fixed by changing the setting tofor_non_interface_members— no ADR required.
Back to: ADR index · Quality gates