Inshiminator is a .NET-first developer toolkit that uses Roslyn analyzers, incremental source generators, and optional code fixes to insert clean shims around hard dependencies without forcing teams into invasive rewrites.
The premise is simple: most applications have business logic directly touching time, files, randomness, environment variables, HTTP, process execution, vendor SDKs, and other things that make code hard to test or modernize. Inshiminator finds those dependency seams, generates strongly typed abstractions and implementations, and helps teams move from direct dependency usage to controlled boundaries.
The name works because the tool literally puts shims in things.
Inshiminator uses Roslyn analyzers and source generators to detect hard dependencies in .NET code and generate shims, fakes, adapters, diagnostics, and guardrails around them.
Inshiminator is an analyzer-guided shim generation toolkit for .NET. It detects direct usage of unstable or external dependencies, emits diagnostics with actionable fixes, generates compile-time shim abstractions and implementations, and helps teams enforce dependency-boundary rules incrementally.
It sees your code raw-dogging DateTime.UtcNow and quietly slides a shim into the situation.
Instead of manually hunting every untestable dependency, writing repetitive interfaces, wiring implementations, and building fakes, teams can let Inshiminator generate most of the boring boundary code while analyzers keep new violations from leaking in.
Inshiminator turns hard dependencies into generated, governed, testable boundaries.
The elegant version of Inshiminator should not behave like a blunt CLI refactoring hammer. It should behave like a compiler-adjacent assistant.
The best architecture is:
- Analyzers detect dependency boundary smells.
- Diagnostics explain the problem and suggest the proper shim.
- Source generators generate the shim surface.
- Code fixes optionally update safe call sites.
- MSBuild integration makes the generated boundary part of normal builds.
- CLI tooling summarizes, baselines, and governs the system in CI.
This means Inshiminator can work continuously while developers build, test, and edit code. The CLI remains useful, but the core product is not a one-time migration tool. It becomes a development-time boundary system.
Roslyn analyzers detect direct dependency usage and classify it by category, severity, layer, and fixability.
Incremental source generators emit strongly typed shims, default implementations, fakes, test helpers, DI extension methods, and optional decorators.
Diagnostics and code fixes guide developers from direct usage toward generated shims.
Analyzer severity, baselines, CI guardrails, and SARIF output enforce standards over time.
The system supports custom shim providers, organization policy packs, templates, and category-specific generators.
A shim is a generated boundary between application code and a dependency that is difficult to control directly.
A shim may include:
- An abstraction interface
- A default production implementation
- A fake implementation for tests
- A deterministic test builder
- A DI registration method
- A telemetry decorator
- A resilience decorator
- A record/replay adapter
- A compatibility adapter
- Analyzer rules that discourage bypassing the shim
A hard dependency is direct usage of a system, runtime, external service, or concrete library that makes code difficult to test, observe, replace, or migrate.
Examples:
DateTime.UtcNow
Guid.NewGuid()
Random.Shared
File.ReadAllText(path)
Environment.GetEnvironmentVariable("API_KEY")
new HttpClient()
Process.Start(...)
Thread.Sleep(...)
new SqlConnection(connectionString)
VendorSdk.StaticClient.DoThing(...)Many shims are repetitive. Developers should not need to hand-write a new ISystemClock, SystemClock, FakeSystemClock, and service registration in every project.
Source generators let Inshiminator provide this boundary code at compile time, consistently and with minimal friction.
Recommended package structure:
Inshiminator.Abstractions
Inshiminator.Analyzers
Inshiminator.Generators
Inshiminator.CodeFixes
Inshiminator.Testing
Inshiminator.DependencyInjection
Inshiminator.Cli
Inshiminator.MSBuild
Inshiminator.PolicyPacks.Default
Inshiminator.PolicyPacks.CleanArchitecture
Contains stable contracts used by generated code and optional runtime helpers.
This package should be small, dependency-light, and safe for production projects.
Potential contents:
public interface IClock
{
DateTimeOffset UtcNow { get; }
}
public interface IGuidGenerator
{
Guid NewGuid();
}
public interface IRandomSource
{
int Next();
int Next(int maxValue);
int Next(int minValue, int maxValue);
}Design note: some shims can be generated without requiring this package, but a shared abstractions package gives teams stable common contracts.
Contains Roslyn analyzers that detect direct hard dependency usage.
Responsibilities:
- Detect direct dependency calls
- Emit diagnostics
- Apply configurable severity
- Understand project/layer context where possible
- Suppress generated code
- Support
.editorconfigconfiguration - Support baseline metadata
Contains incremental source generators that generate shim code based on configuration, attributes, and detected project capabilities.
Responsibilities:
- Generate interfaces
- Generate production implementations
- Generate fakes where appropriate
- Generate DI registration extensions
- Generate test helpers
- Generate optional decorators
- Emit source in deterministic form
Contains code fix providers for safe and local transformations.
Responsibilities:
- Replace direct
DateTime.UtcNowwith injectedIClock.UtcNowwhere constructor injection is straightforward - Replace
Guid.NewGuid()with injectedIGuidGenerator.NewGuid()where safe - Replace
new HttpClient()with an injected client or generated client boundary only where safe - Add constructor parameters
- Add private fields
- Add using directives
- Avoid risky wide refactors
Contains test support utilities and generated fake conventions.
Potential contents:
FakeClock
FakeGuidGenerator
FakeRandomSource
FakeEnvironmentReader
FakeFileSystem
InshimTestHarnessContains optional DI helpers for Microsoft.Extensions.DependencyInjection.
Potential contents:
services.AddInshiminatorClock();
services.AddInshiminatorGuidGenerator();
services.AddInshiminatorDefaults();The source generator may also generate application-specific extension methods.
Provides reporting, baselining, CI guard mode, and project-wide analysis.
Responsibilities:
- Run analyzers from command line
- Generate reports
- Create baselines
- Enforce no-new-violations gates
- Print shim coverage metrics
- Export SARIF/JSON/Markdown
Provides build integration, defaults, generated file materialization options, and package ergonomics.
Responsibilities:
- Add analyzer/generator references
- Configure generated output behavior
- Support
EmitCompilerGeneratedFiles - Surface reports in artifacts
- Support central package configuration
Provides prebuilt rule configurations and conventions.
Examples:
- Default policy pack
- Clean Architecture policy pack
- Strict testability policy pack
- Legacy migration policy pack
- Minimal API policy pack
A developer installs Inshiminator into a project:
dotnet add package Inshiminator.Analyzers
dotnet add package Inshiminator.GeneratorsThen builds:
dotnet buildThe compiler emits diagnostics:
INSHIM001 Direct system clock usage detected.
OrderService.cs(42,21): DateTime.UtcNow should be accessed through IClock.
Suggested shim: clock
Code fix available: Inject IClock and replace usage.
The generator emits the required shim code:
namespace MyApp.Generated.Shims;
public interface IClock
{
DateTimeOffset UtcNow { get; }
}
public sealed class SystemClock : IClock
{
public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
}The developer applies a code fix, reviews the change, and commits.
A CLI refactor is episodic. Developers run it once, then the codebase drifts.
Analyzer/generator integration is continuous. Every build can detect violations, generate supporting code, and enforce boundaries.
This gives Inshiminator three major advantages:
- Lower friction: generated shims appear as part of the normal build.
- Incremental adoption: existing violations can be baselined while new ones are blocked.
- Long-term governance: analyzers keep the codebase from regressing.
Use incremental source generators.
Reasons:
- Better performance
- Deterministic pipeline
- Works well with IDEs
- Supports incremental updates
- Avoids full-project regeneration on every edit
Generator inputs may include:
- MSBuild properties
- Additional files
.editorconfigoptions- Attributes in source code
- Referenced package detection
- Project target framework
- Nullable context
- Existing user-defined abstractions
Support multiple configuration paths in order of precedence:
.editorconfig- MSBuild properties
inshiminator.jsonas an additional file- Attributes in source code
- Built-in defaults
Generated code references Inshiminator.Abstractions.
Pros:
- Stable contracts
- Easy cross-project use
- Less generated code
Cons:
- Runtime package dependency
Generated code emits all required abstractions directly into the target project.
Pros:
- No runtime dependency
- Good for libraries and strict environments
Cons:
- More generated code
- Cross-project type sharing requires care
Use shared abstractions for common shims and generated application-specific wrappers for specialized boundaries.
This should be the recommended default.
MVP generated categories:
- Clock
- GUID generation
- Random source
- Environment reader
- File system facade
- Process runner
- Delay provider
- HTTP client boundary helpers
Future generated categories:
- Vendor SDK adapters
- Queue clients
- Database connection factories
- API compatibility adapters
- Record/replay wrappers
- OpenAPI client shims
Attributes allow developers to opt into generation explicitly.
Example:
[assembly: GenerateInshim("clock")]
[assembly: GenerateInshim("guid")]
[assembly: GenerateInshim("environment")]This tells the generator to emit selected shim categories.
Example:
[GenerateShimFor(typeof(StripeClient))]
public partial interface IStripeGateway;The generator could inspect StripeClient and scaffold a wrapper pattern.
This should be future scope because arbitrary SDK wrapping gets complicated quickly.
Example:
[ShimBoundary("payments")]
public partial class PaymentGateway;Could be used later to generate decorators, recording wrappers, or telemetry layers.
Example:
[assembly: GenerateInshimDependencyInjection]Generator emits:
public static partial class InshiminatorServiceCollectionExtensions
{
public static IServiceCollection AddGeneratedShims(this IServiceCollection services)
{
services.AddSingleton<IClock, SystemClock>();
services.AddSingleton<IGuidGenerator, GuidGenerator>();
return services;
}
}Analyzer and generator options should work naturally through .editorconfig.
Example:
# Inshiminator
inshiminator_generation_mode = hybrid
inshiminator_namespace = MyApp.Generated.Shims
inshiminator_generate_clock = true
inshiminator_generate_guid = true
inshiminator_generate_random = true
inshiminator_generate_environment = true
inshiminator_generate_filesystem = true
inshiminator_generate_di = true
# Analyzer severity
dotnet_diagnostic.INSHIM001.severity = warning
dotnet_diagnostic.INSHIM002.severity = warning
dotnet_diagnostic.INSHIM006.severity = error
# Layer-specific conventions
inshiminator_domain_projects = **/*.Domain.csproj
inshiminator_application_projects = **/*.Application.csproj
inshiminator_infrastructure_projects = **/*.Infrastructure.csproj
inshiminator_tests_projects = **/*Tests.csproj;**/*.Tests.csprojPotential issue: .editorconfig globbing and MSBuild project awareness can get awkward. Use it for analyzer options, but allow richer project structure config in inshiminator.json.
Support MSBuild properties for package ergonomics.
Example:
<PropertyGroup>
<InshiminatorGenerationMode>Hybrid</InshiminatorGenerationMode>
<InshiminatorNamespace>MyApp.Generated.Shims</InshiminatorNamespace>
<InshiminatorGenerateClock>true</InshiminatorGenerateClock>
<InshiminatorGenerateGuid>true</InshiminatorGenerateGuid>
<InshiminatorGenerateDependencyInjection>true</InshiminatorGenerateDependencyInjection>
<InshiminatorEmitReports>true</InshiminatorEmitReports>
</PropertyGroup>Support item-based configuration:
<ItemGroup>
<InshiminatorShim Include="Clock" />
<InshiminatorShim Include="Guid" />
<InshiminatorShim Include="FileSystem" />
</ItemGroup>This fits well with enterprise repo standards and central Directory.Build.props usage.
Code fixes should be conservative.
The tool should only auto-transform code when the change is highly predictable. Risky changes should produce guidance, not surprise rewrites.
Original:
public sealed class OrderService
{
public Order CreateOrder()
{
return new Order
{
CreatedAt = DateTimeOffset.UtcNow
};
}
}Fixed:
public sealed class OrderService
{
private readonly IClock _clock;
public OrderService(IClock clock)
{
_clock = clock;
}
public Order CreateOrder()
{
return new Order
{
CreatedAt = _clock.UtcNow
};
}
}A code fix may be applied automatically when:
- The containing type is instantiable through constructor injection
- The type already uses dependency injection patterns
- Adding a constructor parameter does not conflict with existing constructors
- The generated shim type is available
- The replacement expression is semantically straightforward
A code fix should not be automatically applied when:
- The usage occurs in a static method without clear injection path
- The type has many constructors and no obvious primary path
- The usage occurs in generated code
- The dependency is security-sensitive
- The replacement requires broad architectural changes
Support fix-all only for narrow categories:
- Clock
- GUID generation
- Simple environment reader usage
Avoid fix-all for:
- File system
- HTTP
- Database connections
- Vendor SDKs
- Process execution
Those changes often require intent, not just syntax.
Use stable diagnostic IDs.
INSHIM001 Direct system clock usage
INSHIM002 Direct GUID generation
INSHIM003 Direct randomness usage
INSHIM004 Direct file system usage
INSHIM005 Direct environment usage
INSHIM006 Direct HttpClient construction
INSHIM007 Direct process execution
INSHIM008 Blocking sleep detected
INSHIM009 Direct console I/O usage
INSHIM010 Direct database connection construction
INSHIM011 Direct vendor SDK construction
INSHIM012 Shim generated but not registered
INSHIM013 Shim generated but unused
INSHIM014 Direct dependency usage violates configured layer policy
INSHIM015 Generated shim conflicts with existing type
Diagnostics should be direct and actionable.
Example:
INSHIM001: Direct system clock usage detected.
Use IClock instead of DateTimeOffset.UtcNow so time can be controlled in tests.
Each diagnostic should include:
- Category
- Suggested shim
- Safety level
- Code fix availability
- Project layer if known
- Documentation URL
- Whether baseline suppression is active
Generated abstraction:
public interface IClock
{
DateTimeOffset UtcNow { get; }
DateTimeOffset Now { get; }
}Generated implementation:
public sealed class SystemClock : IClock
{
public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
public DateTimeOffset Now => DateTimeOffset.Now;
}Generated fake:
public sealed class FakeClock : IClock
{
public DateTimeOffset UtcNow { get; private set; }
public DateTimeOffset Now => UtcNow.ToLocalTime();
public FakeClock(DateTimeOffset? utcNow = null)
{
UtcNow = utcNow ?? DateTimeOffset.UnixEpoch;
}
public void SetUtcNow(DateTimeOffset value)
{
UtcNow = value;
}
public void Advance(TimeSpan value)
{
UtcNow = UtcNow.Add(value);
}
}public interface IGuidGenerator
{
Guid NewGuid();
}
public sealed class GuidGenerator : IGuidGenerator
{
public Guid NewGuid() => Guid.NewGuid();
}Fake:
public sealed class FakeGuidGenerator : IGuidGenerator
{
private readonly Queue<Guid> _values = new();
public FakeGuidGenerator Enqueue(Guid value)
{
_values.Enqueue(value);
return this;
}
public Guid NewGuid()
{
if (_values.Count == 0)
throw new InvalidOperationException("No fake GUID values were configured.");
return _values.Dequeue();
}
}public interface IEnvironmentReader
{
string? GetEnvironmentVariable(string variable);
string MachineName { get; }
string CurrentDirectory { get; }
}File system shims can become large. The MVP should generate a focused facade rather than clone all System.IO APIs.
Recommended MVP abstraction:
public interface IFileSystem
{
bool FileExists(string path);
string ReadAllText(string path);
Task<string> ReadAllTextAsync(string path, CancellationToken cancellationToken = default);
void WriteAllText(string path, string contents);
Task WriteAllTextAsync(string path, string contents, CancellationToken cancellationToken = default);
IEnumerable<string> EnumerateFiles(string path, string searchPattern = "*");
}Future option: integrate with or generate adapters for System.IO.Abstractions.
public interface IDelayProvider
{
Task Delay(TimeSpan delay, CancellationToken cancellationToken = default);
}
public sealed class SystemDelayProvider : IDelayProvider
{
public Task Delay(TimeSpan delay, CancellationToken cancellationToken = default)
=> Task.Delay(delay, cancellationToken);
}This helps replace direct Task.Delay and Thread.Sleep in testable workflows.
public interface IProcessRunner
{
Task<ProcessResult> RunAsync(ProcessRequest request, CancellationToken cancellationToken = default);
}This category should be generated but not aggressively auto-applied.
When enabled, the generator emits:
public static class InshiminatorGeneratedServiceCollectionExtensions
{
public static IServiceCollection AddInshiminatorGeneratedShims(this IServiceCollection services)
{
services.AddSingleton<IClock, SystemClock>();
services.AddSingleton<IGuidGenerator, GuidGenerator>();
services.AddSingleton<IEnvironmentReader, SystemEnvironmentReader>();
services.AddSingleton<IDelayProvider, SystemDelayProvider>();
return services;
}
}Analyzer should detect when a generated shim exists but is not registered in known DI startup flows.
Possible diagnostic:
INSHIM012 Generated shim IClock does not appear to be registered in IServiceCollection.
This diagnostic should be informational by default because DI registration can be dynamic.
Support common patterns:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddInshiminatorGeneratedShims();services.AddInshiminatorGeneratedShims();The CLI still matters, but it should not be the center of the product.
The CLI should:
- Run project-wide analysis
- Generate reports
- Create and update baselines
- Produce architecture summaries
- Export SARIF
- Support CI guard mode
- Help initialize configuration
- Materialize generated code if requested
inshim init
inshim analyze ./src
inshim report ./src --format markdown
inshim baseline create ./src
inshim guard ./src --baseline inshim.baseline.json
inshim doctorThe CLI should not be the main refactoring engine. Refactorings should live in analyzers and code fixes so they work inside IDEs and normal builds.
Brownfield codebases may have hundreds or thousands of existing direct dependency usages. Blocking all violations on day one guarantees adoption will die in a Jira comment thread.
Baselines allow teams to say:
We know the old violations exist. Do not allow new ones.
Example:
{
"version": 1,
"createdAt": "2026-05-07T00:00:00Z",
"violations": [
{
"id": "INSHIM001",
"file": "src/MyApp/Orders/OrderService.cs",
"line": 42,
"category": "clock",
"fingerprint": "abc123"
}
]
}Violation fingerprints should be stable enough to survive line movement where possible.
Fingerprint inputs may include:
- Diagnostic ID
- Symbol name
- Containing type
- Containing member
- Syntax kind
- Normalized expression
- File path
Avoid line number as the primary identity.
inshim guard --mode warn
inshim guard --mode fail-on-new
inshim guard --mode fail-on-any
inshim guard --mode ratchetRatchet mode should fail if the total violation count increases and pass if it decreases or remains the same.
Required:
- Console
- JSON
- Markdown
- SARIF
Future:
- HTML
- Mermaid graphs
- DocFX integration
- GitHub PR comments
- Azure DevOps annotations
# Inshiminator Boundary Report
## Summary
- Total findings: 128
- New findings: 3
- Baseline findings: 125
- Shim coverage: 34%
## Findings by Category
| Category | Findings | Shimmed | Coverage |
|---|---:|---:|---:|
| Clock | 22 | 7 | 24% |
| File system | 41 | 3 | 7% |
| HTTP | 8 | 4 | 50% |
## Highest Risk Findings
...
## Recommended Next Shims
...Shim coverage should help teams measure progress.
Example:
Clock
- Direct usages: 22
- Shimmed usages: 17
- Coverage: 43%
- Recommended next action: Apply clock shim code fix in Application project
A direct file read inside an infrastructure adapter may be fine. A direct file read inside a domain service is a smell wearing a trench coat.
Severity should depend on where usage occurs.
Inshiminator should infer layers from:
- Project name
- Assembly name
- Namespace
- Folder path
- Configuration
- Attributes
- MSBuild properties
Examples:
*.Domain
*.Application
*.Infrastructure
*.Web
*.Api
*.Worker
*.Tests
{
"layers": {
"domain": {
"projects": ["**/*.Domain.csproj"],
"disallowedCategories": ["filesystem", "environment", "http", "database", "process"]
},
"application": {
"projects": ["**/*.Application.csproj"],
"disallowedCategories": ["filesystem", "environment", "process"]
},
"infrastructure": {
"projects": ["**/*.Infrastructure.csproj"],
"allowedCategories": ["filesystem", "environment", "http", "database", "process"]
}
}
}Future extensibility should allow custom providers.
Conceptual shape:
public interface IShimProvider
{
string Category { get; }
ImmutableArray<DiagnosticDescriptor> Diagnostics { get; }
void Analyze(ShimAnalysisContext context);
void Generate(ShimGenerationContext context);
}Roslyn analyzers and generators have their own constraints, so the actual implementation may require separate analyzer and generator extension points.
- Analyzer provider
- Generator provider
- Code fix provider
- Report provider
- Policy provider
- Template provider
Providers should be distributable as NuGet packages.
Example:
Inshiminator.Provider.AzureStorage
Inshiminator.Provider.Stripe
Inshiminator.Provider.SendGrid
Inshiminator.Provider.AmazonS3
Inshiminator.Provider.LegacySoap
Vendor SDKs often enter application code directly:
var client = new StripeClient(apiKey);
await client.Charges.CreateAsync(...);This creates coupling, test complexity, and migration pain.
Inshiminator could generate vendor-specific gateway wrappers:
public interface IPaymentGateway
{
Task<PaymentResult> ChargeAsync(PaymentRequest request, CancellationToken cancellationToken = default);
}But this should not be blindly generated from every SDK method. Useful vendor shims require domain intent.
Start with detection and scaffolding:
Detected direct StripeClient usage.
Suggested action: Create a payment gateway boundary.
Generated scaffold available.
Let the developer choose the domain-specific shape.
Once calls are routed through shims, Inshiminator can support runtime record/replay.
Example:
inshim record -- dotnet test
inshim replay -- dotnet test- Record HTTP responses
- Replay vendor API calls
- Capture file reads
- Capture message queue interactions
- Make integration tests deterministic
- Debug production-like workflows locally
Runtime record/replay depends on shims existing first. That means the analyzer/generator foundation is the right first move.
dotnet add package Inshiminator.Analyzers
dotnet add package Inshiminator.Generators
dotnet add package Inshiminator.DependencyInjection<PropertyGroup>
<InshiminatorGenerateClock>true</InshiminatorGenerateClock>
<InshiminatorGenerateGuid>true</InshiminatorGenerateGuid>
<InshiminatorGenerateDependencyInjection>true</InshiminatorGenerateDependencyInjection>
</PropertyGroup>dotnet buildINSHIM001 Direct system clock usage detected.
Use IClock instead of DateTimeOffset.UtcNow.
IDE action:
Inject IClock and replace DateTimeOffset.UtcNow
builder.Services.AddInshiminatorGeneratedShims();var clock = new FakeClock(DateTimeOffset.Parse("2026-05-07T12:00:00Z"));
var service = new OrderService(clock);Goals:
- Validate Roslyn analyzer/generator architecture
- Confirm code fix feasibility for constructor injection
- Confirm config options through
.editorconfigand MSBuild properties - Confirm generated DI extension strategy
Deliverables:
- Minimal analyzer detecting
DateTimeOffset.UtcNow - Minimal generator emitting
IClockandSystemClock - Minimal code fix replacing one usage in a simple class
- Test project validating analyzer and generator behavior
Acceptance criteria:
- Analyzer emits
INSHIM001for direct clock usage - Generator emits compiling clock shim code
- Code fix updates a simple constructor-injected class
- Analyzer does not report diagnostics in generated code
- Tests verify all of the above
Build analyzers for MVP categories:
- Clock
- GUID
- Random
- Environment
- File system
- HTTP construction
- Process execution
- Blocking sleep
Acceptance criteria:
- Each category has a stable diagnostic ID
- Each diagnostic has documentation metadata
- Analyzer tests cover positive and negative cases
- Generated code is ignored
- Severity can be configured
Build incremental generator for common shims:
- Clock
- GUID generator
- Random source
- Environment reader
- Delay provider
- Process runner scaffold
- DI extension generation
Acceptance criteria:
- Generator supports MSBuild property configuration
- Generator supports
.editorconfigconfiguration where appropriate - Generated code is deterministic
- Generated code compiles under nullable enabled
- Generated code supports netstandard2.0-compatible output where possible
- Tests validate generated source snapshots
Build conservative code fixes:
- Clock replacement
- GUID replacement
- Delay provider replacement for simple
Task.Delay - Environment variable replacement for simple reads
Acceptance criteria:
- Code fixes work in simple constructor-injected classes
- Code fixes avoid static classes unless explicitly supported
- Code fixes add fields and constructor parameters safely
- Fix-all is supported only for safe categories
- Risky changes produce diagnostics without fix-all
Build CLI for project-wide use:
inshim analyze
inshim report
inshim baseline create
inshim guard
inshim doctorAcceptance criteria:
- CLI can analyze a solution
- CLI can emit JSON report
- CLI can emit Markdown report
- CLI can emit SARIF report
- CLI can create a baseline
- CLI can fail on new violations
Build default and Clean Architecture policy packs.
Acceptance criteria:
- Default pack works for general projects
- Clean Architecture pack treats Domain/Application/Infrastructure differently
- Policy can be applied through MSBuild or config file
- CI guard respects policy pack severity
Build docs and examples:
- README
- Getting started
- Analyzer rule docs
- Generator configuration docs
- Code fix examples
- CI examples
- Clean Architecture sample
- Legacy app sample
- Minimal API sample
Acceptance criteria:
- Docs include before/after examples
- Each diagnostic has documentation
- Samples compile and test in CI
- README clearly explains analyzer/generator workflow
Use Roslyn analyzer testing packages.
Test cases:
- Direct usage detected
- Allowed usage ignored
- Generated code ignored
- Severity config respected
- Layer policy respected
- Suppression works
Test cases:
- Generated source matches snapshot
- Generated source compiles
- MSBuild properties affect output
.editorconfigoptions affect output- Nullable output is correct
- Duplicate generation is avoided
Test cases:
- Before/after source transformation
- Constructor injection added
- Existing constructor updated
- Existing field naming conflicts handled
- Unsafe contexts are not fixed
- Fix-all behavior is constrained
Test cases:
- Analyze sample solution
- Emit report formats
- Create baseline
- Guard new violation
- Ratchet mode
- Exit codes
Create sample apps:
samples/MinimalApiWithClock
samples/CleanArchitectureApp
samples/LegacyConsoleApp
samples/WorkerServiceWithDelays
samples/HttpClientUsageApp
Each sample should validate realistic usage.
- Inshiminator detects direct system clock usage.
- Inshiminator detects direct GUID generation.
- Inshiminator detects direct randomness usage.
- Inshiminator detects direct file system usage.
- Inshiminator detects direct environment variable usage.
- Inshiminator detects direct
new HttpClient()usage. - Inshiminator detects direct process execution.
- Inshiminator detects blocking sleeps.
- Inshiminator suppresses diagnostics for generated code.
- Inshiminator diagnostics include actionable messages.
- Inshiminator diagnostics include stable IDs.
- Inshiminator severities are configurable.
- Inshiminator generates clock shims.
- Inshiminator generates GUID shims.
- Inshiminator generates random source shims.
- Inshiminator generates environment reader shims.
- Inshiminator generates delay provider shims.
- Inshiminator generates DI registration extensions.
- Generated code compiles without manual edits.
- Generated code is deterministic.
- Generated code supports nullable reference types.
- Generated code avoids duplicate type conflicts where possible.
- Inshiminator can replace simple
DateTimeOffset.UtcNowusage withIClock.UtcNow. - Inshiminator can replace simple
Guid.NewGuid()usage withIGuidGenerator.NewGuid(). - Inshiminator can add constructor parameters for simple injectable classes.
- Inshiminator does not apply unsafe transformations automatically.
- Inshiminator supports fix-all only for safe categories.
- Inshiminator CLI can analyze a solution.
- Inshiminator CLI can generate JSON reports.
- Inshiminator CLI can generate Markdown reports.
- Inshiminator CLI can generate SARIF reports.
- Inshiminator CLI can create a baseline.
- Inshiminator CLI can fail CI on new violations.
- Inshiminator CLI can run in warning-only mode.
- Existing violations can be baselined.
- New violations can fail CI.
- Violation counts can be ratcheted downward.
- Policy packs can alter severity by layer.
- Reports show category counts and shim coverage.
Source generators can only add code. They cannot change call sites.
Mitigation:
- Use analyzers and code fixes for call-site changes.
- Use source generators for boundary code.
- Use CLI reporting for larger migration plans.
Updating constructors is easy in simple classes and messy in real applications.
Mitigation:
- Keep code fixes conservative.
- Support safe cases first.
- Emit guidance for complex cases.
- Avoid pretending every refactor is mechanical.
Teams may already have their own abstractions.
Mitigation:
- Allow using existing types.
- Allow namespace/type customization.
- Support generated and external abstraction modes.
- Support suppression and mapping configuration.
System.IO is large.
Mitigation:
- Generate focused facades for common operations.
- Support integration with existing libraries later.
- Do not attempt full API parity in MVP.
Automatically wrapping every SDK method can produce useless abstractions.
Mitigation:
- Detect and scaffold first.
- Let developers define domain gateway shape.
- Add provider-specific packages only after clear use cases.
Too many warnings can make developers ignore the tool.
Mitigation:
- Start with warnings and baselines.
- Support layer-aware severity.
- Provide clear messages.
- Make rules easy to configure.
/src
/Inshiminator.Abstractions
/Inshiminator.Analyzers
/Inshiminator.Generators
/Inshiminator.CodeFixes
/Inshiminator.Testing
/Inshiminator.DependencyInjection
/Inshiminator.Cli
/Inshiminator.MSBuild
/Inshiminator.PolicyPacks.Default
/Inshiminator.PolicyPacks.CleanArchitecture
/tests
/Inshiminator.Analyzers.Tests
/Inshiminator.Generators.Tests
/Inshiminator.CodeFixes.Tests
/Inshiminator.Cli.Tests
/Inshiminator.IntegrationTests
/samples
/MinimalApiWithClock
/CleanArchitectureApp
/LegacyConsoleApp
/WorkerServiceWithDelays
/HttpClientUsageApp
/docs
/rules
/getting-started
/configuration
/samples
| ID | Name | Default Severity | Code Fix | Generator Support |
|---|---|---|---|---|
| INSHIM001 | Direct system clock usage | Warning | Yes | Yes |
| INSHIM002 | Direct GUID generation | Warning | Yes | Yes |
| INSHIM003 | Direct randomness usage | Warning | Partial | Yes |
| INSHIM004 | Direct file system usage | Warning | No MVP | Yes |
| INSHIM005 | Direct environment usage | Warning | Partial | Yes |
| INSHIM006 | Direct HttpClient construction |
Error | Partial | Partial |
| INSHIM007 | Direct process execution | Warning | No MVP | Yes |
| INSHIM008 | Blocking sleep detected | Warning | Partial | Yes |
| INSHIM009 | Direct console I/O usage | Info | No MVP | Future |
| INSHIM010 | Direct database connection construction | Info | No MVP | Future |
| INSHIM011 | Direct vendor SDK construction | Info | No MVP | Future |
| INSHIM012 | Generated shim not registered | Info | Yes | Yes |
| INSHIM013 | Generated shim unused | Info | No | Yes |
| INSHIM014 | Layer policy violation | Configurable | No | N/A |
| INSHIM015 | Generated shim type conflict | Error | No | Yes |
Inshiminator
inshim
Serious:
Generated dependency boundaries for testable .NET systems.
Funny:
Add seams to code that was born without them.
More direct:
Find hard dependencies. Generate shims. Govern the boundary.
Darkly accurate:
Your code has been touching infrastructure unsupervised.
# Inshiminator
Inshiminator is an analyzer-guided shim generation toolkit for .NET.
It finds direct dependency usage like `DateTime.UtcNow`, `Guid.NewGuid()`, `File.ReadAllText`, and `new HttpClient()`, then generates the shims, fakes, and diagnostics needed to make those dependencies testable and governable.The best version of Inshiminator is not a one-shot CLI that rewrites code. It is a compiler-integrated boundary system.
Use:
- Roslyn analyzers to detect hard dependencies
- Incremental source generators to emit shims and fakes
- Code fixes to perform safe call-site migrations
- MSBuild integration to make it normal build behavior
- CLI tooling for reports, baselines, and CI governance
This gives the product a strong identity:
Inshiminator is the tool that finds places where your app needs a shim, generates the shim, and keeps future code from bypassing it.
That is a real product. The name is still stupid. That is an asset.