Quick Start#
The Increment static class is the main entry point. It exposes a fluent API
and a JustDoIt shorthand. Both go through the full enterprise pipeline —
caching, validation, strategy selection, middleware, event sourcing, telemetry.
There is no escape.
// The fluent way. var result = await Increment.ThisNumber(41) .AsUser("DemoUser") .BecauseINeedTo("calculate the meaning of life + 1") .Urgently() .PleaseAsync(); Console.WriteLine(result.ResultValue); // 42 // The impatient way. Still goes through the full pipeline. var result = await Increment.JustDoIt(99); // Batch mode. var batch = await Increment.ThoseNumbers(1, 2, 3, 4, 5); // Results: 1→2, 2→3, 3→4, 4→5, 5→6
The Justification field is technically optional.
The default orchestrator is created lazily on first use via Lazy<IncrementOrchestrator>
and is configured with WithFullEnterpriseConfiguration() — all six strategies,
all four validators, all three middleware layers, caching, telemetry, and event sourcing.
Fluent Builder#
IncrementOrchestratorBuilder lets you compose a custom orchestrator.
Call Create(), chain the desired components, then Build().
var orchestrator = IncrementOrchestratorBuilder.Create() .WithClassicStrategy() .WithBitwiseStrategy() .WithMonteCarloStrategy() // live dangerously .WithOverflowProtection() .WithSuperstitionProtection() .WithRateLimiting(maxPerMinute: 10) .WithLogging() .WithPremiumExperience(delayMs: 200) .WithRetryPolicy() .WithCaching(maxSize: 50000) .WithTelemetry() .WithEventSourcing() .Build(); // Or: the nuclear option. var orchestrator = IncrementOrchestratorBuilder.Create() .WithFullEnterpriseConfiguration() .Build();
Builder Methods
| Method | Description |
|---|---|
| WithClassicStrategy() | Adds ClassicIncrementStrategy — uses the + operator like a normal person |
| WithBitwiseStrategy() | Adds BitwiseIncrementStrategy — XOR/AND carry loop |
| WithDoubleNegationStrategy() | Adds DoubleNegationIncrementStrategy — computes n − (−1) |
| WithPeanoAxiomStrategy() | Adds PeanoAxiomIncrementStrategy — O(n), counts from zero |
| WithLookupTableStrategy() | Adds LookupTableIncrementStrategy — 2,001-entry pre-computed dict |
| WithMonteCarloStrategy() | Adds MonteCarloIncrementStrategy — stochastic search, O(∞) |
| WithAllStrategies() | Adds all six strategies above |
| WithOverflowProtection() | Adds OverflowValidator |
| WithNegativityProtection(minimum) | Adds NegativityValidator with optional floor |
| WithSuperstitionProtection() | Adds SuperstitionValidator — guards against cursed numbers |
| WithRateLimiting(maxPerMinute) | Adds RateLimitValidator, default 60/min |
| WithAllValidators() | Adds all four validators above |
| WithLogging() | Adds LoggingMiddleware (Order: 0) |
| WithPremiumExperience(delayMs) | Adds PremiumExperienceMiddleware, default 100ms delay |
| WithRetryPolicy() | Adds RetryMiddleware (Order: 2) |
| WithCaching(maxSize) | Adds InMemoryIncrementCache, default capacity 10,000 |
| WithTelemetry() | Adds IncrementTelemetryCollector |
| WithEventSourcing() | Adds IncrementEventStore |
| WithObserver(observer) | Adds a custom IIncrementObserver |
| WithFullEnterpriseConfiguration() | All of the above. Maximum enterprise. No compromises. |
Increment Strategies#
Six strategies, all arriving at the same answer. Strategies are sorted by
Priority descending — the orchestrator picks the first one where
CanHandle(value) returns true. Pass a name to
.UsingStrategy("MonteCarlo") to override selection.
Uses the + operator. Boring, but reliable. Implements IReversibleIncrementStrategy<T> so it can also decrement for rollback support.
XOR sets bits without carry, AND finds carry bits, shifts left. Iterates until carry is zero. Confuses code reviewers. That's the point.
Computes n − (−1). Subtraction of a negative is addition. Mathematically equivalent. Spiritually different. Audit trail includes the proof.
2,001 pre-computed entries, populated at static init. Returns immediately for any value in range. Falls back with a ticket suggestion outside range.
Reconstructs the natural number from zero by applying the successor function n times, then once more. Audit trail cites both Peano axioms. CancellationToken respected mid-proof.
Generates random integers in [0, n+100] until one equals n+1. Knows the answer before starting — it's about the journey. Returns VeryHigh confidence. "It's not stupid, it's stochastic."
MonteCarlo is non-deterministic. Observed range: 0.3ms–847ms for n=5. Do not use in latency-sensitive paths. Or do. I'm just a sign.
Implementing a Custom Strategy
Implement IIncrementStrategy<T> and register it with .WithStrategy(myStrategy).
public sealed class MyIncrementStrategy : IIncrementStrategy<int> { public string StrategyName => "MyStrategy"; public string StrategyDescription => "My custom approach"; public Version StrategyVersion => new(1, 0, 0); public int Priority => 75; // between LookupTable(80) and Bitwise(50) public bool CanHandle(int value) => value >= 0; public Task<IncrementResult<int>> IncrementAsync( int value, IncrementContext context, CancellationToken ct = default) { context.AddAuditEntry($"MyStrategy: computing {value} + 1"); return Task.FromResult(new IncrementResult<int> { OriginalValue = value, ResultValue = value + 1, IsSuccess = true, StrategyUsed = StrategyName }); } }
Validation Pipeline#
Validators run sequentially before strategy selection. A single failure short-circuits
the pipeline and returns an unsuccessful IncrementResult<T>.
Warnings (e.g. from SuperstitionGuard) are recorded in the audit trail
but do not block execution.
| Validator | Class | Failure condition | Error message |
|---|---|---|---|
| OverflowGuard | OverflowValidator |
value == int.MaxValue | "Consider upgrading to IaaS.BigInteger.Enterprise" |
| NegativityGuard | NegativityValidator |
value < minimumAllowedValue | "This number needs therapy, not incrementing." |
| SuperstitionGuard | SuperstitionValidator |
value ∈ {13, 666, 4, 9, 17, 39, 87} | Warning only — proceeds with caution |
| RateLimiter | RateLimitValidator |
requests > maxPerMinute (default: 60) | "Please wait and reflect on whether you really need all those +1s." |
NegativityValidator accepts a minimumAllowedValue constructor parameter (default int.MinValue). Negative values within the allowed range pass validation but generate an audit note.
Implementing a Custom Validator
public sealed class EvenNumberValidator : IIncrementValidator<int> { public string ValidatorName => "EvenNumberGuard"; public Task<ValidationResult> ValidateAsync( int value, IncrementContext context, CancellationToken ct = default) { if (value % 2 != 0) return Task.FromResult(ValidationResult.Failure( ValidatorName, "Only even numbers may be incremented here.")); return Task.FromResult(ValidationResult.Success(ValidatorName)); } }
Middleware Pipeline#
Middleware wraps the strategy execution in a chain, ordered by Order
ascending. Each piece calls next(value, context) to invoke the rest of
the chain. The default stack runs: Logging → PremiumExperience → Retry → Strategy.
| Middleware | Order | What it does |
|---|---|---|
| LoggingMiddleware | 0 | Logs input value, type, requested-by, correlation ID, thread ID, memory before/after, duration, and result. Everything. EVERYTHING. |
| PremiumExperienceMiddleware | 1 | Calls Task.Delay(delayMs) before passing to next. Default: 50ms (100ms if configured via builder). Fast software is suspicious software. |
| RetryMiddleware | 2 | Retries on failure up to MaxRetries times using the policy in IncrementOptions.RetryPolicy. Supports Linear, ExponentialBackoff, RandomizedJitter, and Infinite. |
Retry Delay Calculation
var delay = context.Options.RetryPolicy switch { RetryPolicy.Linear => TimeSpan.FromMilliseconds(100 * attempt), RetryPolicy.ExponentialBackoff => TimeSpan.FromMilliseconds(Math.Pow(2, attempt) * 50), RetryPolicy.RandomizedJitter => TimeSpan.FromMilliseconds(new Random().Next(50, 500)), _ => TimeSpan.Zero };
Event Sourcing#
Every increment operation emits domain events appended to an IncrementEventStore.
Each stream is keyed by the RequestId (correlation ID). Events are stored
in a ConcurrentDictionary protected by a ReaderWriterLockSlim.
The global sequence number is thread-safe via Interlocked.Increment.
var store = orchestrator.GetEventStore(); // All events across all streams, ordered by global sequence number. var all = store.GetAllEvents(); // Events for a specific operation. var stream = store.GetStream(request.RequestId); Console.WriteLine($"Total events: {store.TotalEvents}"); Console.WriteLine($"Total streams: {store.TotalStreams}");
Increment (facade)#
static class Increment — the top-level entry point. Uses a
Lazy<IncrementOrchestrator> singleton configured with
WithFullEnterpriseConfiguration().
| Member | Signature | Description |
|---|---|---|
| ThisNumber | IncrementRequestBuilder ThisNumber(int value) | Returns a fluent builder for the given value |
| JustDoIt | Task<IncrementResult<int>> JustDoIt(int value) | Bypasses the fluent API. Still full pipeline. Justification logged as "Suspicious." |
| ThoseNumbers | Task<BatchIncrementResult<int>> ThoseNumbers(params int[] values) | Increments multiple numbers using default BatchOptions |
| ShowArchitecture | string ShowArchitecture() | Returns the ASCII architecture diagram. For presentations and terrifying new hires. |
IncrementRequestBuilder — Fluent Chain
Returned by Increment.ThisNumber(n). All methods return this for chaining. Terminated by PleaseAsync().
| Method | Description |
|---|---|
| .AsUser(string user) | Sets RequestedBy on the request. Defaults to Environment.UserName. |
| .BecauseINeedTo(string justification) | Sets Justification. Logged in audit trail. Optional but your PR reviewer will ask. |
| .WithPriority(Priority priority) | Sets request priority. See Priority enum. |
| .Urgently() | Shorthand for .WithPriority(Priority.Critical). |
| .WheneverYouGetToIt() | Shorthand for .WithPriority(Priority.WheneverYouGetToIt). |
| .UsingStrategy(string name) | Sets IncrementOptions.PreferredStrategy. Falls back to default if strategy can't handle the value. |
| .WithOptions(Action<IncrementOptions>) | Full options override via configure delegate. |
| .PleaseAsync(CancellationToken) | Executes the request. Returns Task<IncrementResult<int>>. |
IncrementResult<T>#
The return type of every increment operation. You don't just get n+1 —
you get n+1 with full provenance. All properties are init-only.
Call ToJson() for indented camelCase JSON serialization.
OriginalValue = 41,
ResultValue = 42,
IsSuccess = true,
ErrorMessage = null,
StrategyUsed = "Classic",
Duration = 00:00:00.0000314,
OperationId = Guid("3f2a1d..."),
Timestamp = 2026-03-18T14:30:00Z,
RetryCount = 0,
WasCached = false,
Confidence = Absolute,// 100 — peer reviewed, published in Nature
AuditTrail = [ ...47 timestamped entries... ],
AppliedPolicy = IncrementPolicy { ... }
}
| Property | Type | Default | Description |
|---|---|---|---|
| OriginalValue | T | — | The value that was passed in |
| ResultValue | T | — | The incremented value (or original on failure) |
| IsSuccess | bool | — | False if validation failed, no strategy matched, or an exception occurred |
| ErrorMessage | string? | null | Human-readable failure reason. Null on success. |
| StrategyUsed | string | "Unknown" | Name of the strategy that executed |
| Duration | TimeSpan | Zero | Time taken inside the strategy (excludes middleware delay) |
| OperationId | Guid | NewGuid() | Unique ID for this result instance |
| Timestamp | DateTimeOffset | UtcNow | When the result was created |
| RetryCount | int | 0 | Number of random attempts (MonteCarlo) or retry attempts |
| WasCached | bool | false | True if served from InMemoryIncrementCache |
| Confidence | ConfidenceLevel | Absolute | How confident we are that n+1 is correct |
| AuditTrail | IReadOnlyList<string> | empty | Timestamped log entries from all pipeline stages |
| AppliedPolicy | IncrementPolicy? | null | The active policy at execution time |
IncrementRequest<T>#
Wraps the input value with metadata. Use the static factory IncrementRequest<T>.Create()
or construct directly with object initializer syntax. All properties are init-only.
| Property | Type | Default | Description |
|---|---|---|---|
| Value | T | — | The number to be incremented |
| RequestId | Guid | NewGuid() | Becomes the event stream key and CorrelationId |
| RequestedBy | string? | null | User or service identity. Used by RateLimitValidator as bucket key. |
| RequestedAt | DateTimeOffset | UtcNow | When the request was created |
| Options | IncrementOptions | new() | Pipeline configuration for this request |
| Justification | string? | null | Why are you incrementing this number. Logged in audit trail. |
| Priority | Priority | Normal | Used for ordering in batch operations |
IncrementOptions#
Per-request pipeline configuration. Pass via .WithOptions(o => { ... })
on the fluent builder, or set directly on IncrementRequest<T>.Options.
All properties are init-only.
| Property | Type | Default | Description |
|---|---|---|---|
| EnableCaching | bool | true | Check InMemoryIncrementCache before running the pipeline |
| EnableAuditTrail | bool | true | Populate IncrementContext.AuditEntries |
| EnableTelemetry | bool | true | Call IIncrementTelemetry methods on each phase |
| AllowRollback | bool | true | Attach rollback strategy info to the result if available |
| MaxRetries | int | 3 | Maximum retry attempts in RetryMiddleware |
| Timeout | TimeSpan | 30s | Not yet enforced on the pipeline — reserved for future use |
| RetryPolicy | RetryPolicy | ExponentialBackoff | Delay strategy between retries |
| PreferredStrategy | string? | null | Strategy name to prefer. Falls back to priority order if unavailable. |
| RunValidation | bool | true | Skip the validator pipeline entirely when false |
| RequireConsensus | bool | false | Reserved — consensus mode not yet implemented |
Batch Processing#
Increment.ThoseNumbers() and IIncrementOrchestrator<T>.OrchestrateBatchAsync()
process multiple requests concurrently using a SemaphoreSlim to cap parallelism
at BatchOptions.MaxDegreeOfParallelism.
var batch = await Increment.ThoseNumbers(1, 2, 3, 4, 5); Console.WriteLine($"Requests: {batch.TotalRequests}"); Console.WriteLine($"Successes: {batch.SuccessCount}"); Console.WriteLine($"Success rate: {batch.SuccessRate:P0}"); Console.WriteLine($"Total time: {batch.TotalDuration}");
BatchOptions
| Property | Type | Default | Description |
|---|---|---|---|
| MaxDegreeOfParallelism | int | ProcessorCount | SemaphoreSlim initial count — max concurrent increments |
| StopOnFirstFailure | bool | false | Throws IncrementOperationException and halts remaining tasks on any failure |
| BatchTimeout | TimeSpan | 5 minutes | Reserved — not yet wired to a CancellationTokenSource |
BatchIncrementResult<T>
| Property | Type | Description |
|---|---|---|
| Results | IReadOnlyList<IncrementResult<T>> | All results, order not guaranteed |
| TotalRequests | int | Results.Count |
| SuccessCount | int | Count of results where IsSuccess is true |
| FailureCount | int | Count of results where IsSuccess is false |
| SuccessRate | double | SuccessCount / TotalRequests |
| TotalDuration | TimeSpan | Wall-clock time for the entire batch |
Enums#
ConfidenceLevel
Priority
RetryPolicy
Interfaces#
Good architecture starts with at least 14 interfaces. IaaS ships with exactly 11 public ones — the other 3 are implied by good taste.
| Interface | Type parameter | Purpose |
|---|---|---|
| IIncrementable<T> | T : struct, IComparable<T> | Contract for any value object that can be incremented. Exposes Value and WithValue(T). |
| IIncrementStrategy<T> | T : struct, IComparable<T> | A strategy that knows how to add 1. Exposes name, description, version, priority, CanHandle, and IncrementAsync. |
| IReversibleIncrementStrategy<T> | T : struct, IComparable<T> | Extends IIncrementStrategy<T> with DecrementAsync for rollback support. Only ClassicIncrementStrategy implements this. |
| IIncrementValidator<T> | T : struct, IComparable<T> | Validates whether a value deserves to be incremented. Returns ValidationResult with errors and warnings. |
| IIncrementObserver | none | Receives lifecycle callbacks: before, after, failed, rolled-back. Implement to hook external systems. |
| IIncrementMiddleware<T> | T : struct, IComparable<T> | Wraps strategy execution. Ordered by Order property ascending. Calls next(value, context) to continue the chain. |
| IIncrementPolicyEngine<T> | T : struct, IComparable<T> | Evaluates business rules and returns an IncrementPolicy. Not wired by default — reserved for enterprise use cases. |
| IIncrementConflictResolver<T> | T : struct, IComparable<T> | Resolves disagreements between strategy results. Not wired by default. |
| IIncrementCache<T> | T : struct, IComparable<T> | Cache contract. Default implementation: InMemoryIncrementCache with LRU eviction, 1-hour TTL. |
| IIncrementTelemetry | none | Records attempts, successes, failures, cache hits, durations, and per-strategy usage. Call GetSnapshot() for a point-in-time TelemetrySnapshot. |
| IIncrementOrchestrator<T> | T : struct, IComparable<T> | Top-level coordinator. Exposes OrchestrateAsync, OrchestrateBatchAsync, and OrchestrateWithRollbackAsync. |
Telemetry#
IncrementTelemetryCollector uses Interlocked for all counters
and a ConcurrentBag<TimeSpan> for duration samples. Call
orchestrator.GetTelemetry() to get a point-in-time snapshot.
var snap = orchestrator.GetTelemetry(); Console.WriteLine($"Attempts: {snap.TotalAttempts}"); Console.WriteLine($"Successes: {snap.TotalSuccesses}"); Console.WriteLine($"Failures: {snap.TotalFailures}"); Console.WriteLine($"Cache hits: {snap.CacheHits}"); Console.WriteLine($"Cache misses: {snap.CacheMisses}"); Console.WriteLine($"Avg duration: {snap.AverageDuration}"); Console.WriteLine($"Max duration: {snap.MaxDuration}"); foreach (var (name, count) in snap.StrategyUsage) Console.WriteLine($" {name}: {count} uses"); // Cache statistics var cache = orchestrator.GetCacheStatistics(); Console.WriteLine($"Hit rate: {cache.HitRate:P1}"); Console.WriteLine($"Cache size: {cache.CurrentSize}"); Console.WriteLine($"Evictions: {cache.Evictions}");
Caching#
InMemoryIncrementCache wraps a ConcurrentDictionary keyed
by the input value. Each entry stores the result alongside an expiry timestamp.
Default TTL: 1 hour. Default max size: 10,000 entries. On capacity breach, the entry
with the earliest expiry is evicted — which is enterprise-grade LRU if you squint.
Caching is skipped if IncrementOptions.EnableCaching is false.
The second call for the same value returns a copy of the cached result with
WasCached = true, bypassing all validators, strategy selection,
and middleware.
var first = await Increment.JustDoIt(7); var second = await Increment.JustDoIt(7); Console.WriteLine(first.WasCached); // false Console.WriteLine(second.WasCached); // true Console.WriteLine(second.Duration); // near-zero
Benchmarks#
Measured against n++. IaaS loses in every category except observability, which is not benchmarked because it's immeasurable.
* Benchmarks run on a machine that could have used n++ instead. Note: cache hits are competitive with raw n++, which means the cache technically justifies the entire library.
FAQ#
Is this faster than n++?
No. Cache hits are ~0.002ms, which is competitive. Everything else is not. But it's more observable. When your n++ fails silently at 3AM you'll have nothing. When IaaS fails at 3AM you'll have 47 audit trail entries, a correlation ID, a full event stream, and a ConfidenceLevel to blame.
Why does the MonteCarlo strategy exist?
Why shouldn't it exist? The strategy knows the answer before it starts — var target = value + 1 is literally the first line. It then generates random numbers until it finds it. It's all about the journey.
What exactly are the cursed numbers?
The SuperstitionValidator flags: { 13, 666, 4, 9, 17, 39, 87 }. The selection criteria are not documented. 13 and 666 are obvious. 4 is unlucky in East Asian numerology. 9, 17, 39, and 87 were chosen with the same rigor as the rest of this library.
What happens if no strategy can handle my value?
The orchestrator returns an unsuccessful result with the message: "No strategy available to increment this value. The number is beyond our capabilities. We are sorry." This can happen for values outside −1,000 to 1,000 if only the LookupTable strategy is registered, or for values ≥ 10,000 with PeanoAxiom, or for values ≥ 100 with MonteCarlo. Use ClassicIncrementStrategy if you need to increment very large numbers.
Can I use this in production?
You shouldn't. But technically, yes. Everything is async, thread-safe via ConcurrentDictionary, Interlocked, and ReaderWriterLockSlim. The LookupTable static constructor runs once. The default orchestrator is Lazy<T>. You've been warned about MonteCarlo. The Infinite retry policy is your problem.
What's the ROI?
Incalculable. Literally. We tried to calculate it and the calculator needed IncrementAsAService to add 1 to the result, causing an infinite loop. We consider this a successful deployment.