Skip to content
AI and Code

Maintainability Sensors for .NET and React

13 min read

Maintainability sensors are automated checks that watch what a coding agent did and help it keep the codebase healthy, not just green. They run during the session, in continuous integration, on a schedule, and in production, and split into computational sensors (deterministic, cheap, run every commit) and inferential sensors (model-based, for questions judgement alone can answer). Off-the-shelf tools form the baseline, but the highest-value sensors are usually the ones you build for your own codebase, which AI coding now makes cheap enough to be worth it.

  • A green build is a narrow signal. It proves the code compiled and tests passed, not that the change preserved intent.
  • The biggest wins are custom, codebase-specific sensors (accessibility, architecture, SQL performance, end-to-end, and integration), not off-the-shelf libraries.
  • Computational sensors come first and gate the merge. Inferential sensors run after, grounded in their output.
  • Coverage lies about AI-written tests. Mutation testing is the sensor that catches weak assertions.

Our Claude Code development service builds this sensor layer into client codebases as standard.

This guide is the .NET and React companion to our harness engineering guide. That guide sets out the model of guides and sensors. This one goes deep on the sensor half for a Microsoft stack, with the specific tools we run in production.

What are maintainability sensors for coding agents?

Maintainability sensors are automated checks that observe what a coding agent produced and signal whether the change keeps the codebase healthy over time. The term comes from Birgitta Böckeler’s 27 May 2026 article on maintainability sensors for coding agents at Thoughtworks. They are the feedback half of a coding-agent harness, where guides are the feedforward half.

Sensors run at four points in the lifecycle:

  • In the coding session, so the agent gets a failure message and self-corrects before a human sees the change.
  • In continuous integration, so a merge is gated on the slower checks.
  • On a schedule, for expensive analysis that does not need to run on every commit.
  • In production, where runtime signals feed back into the next change.

They fall into two kinds. Computational sensors are deterministic: type checkers, linters, structural tests, mutation testing, and schema diffs. They are cheap, fast, and truthful. Inferential sensors use a language model to judge things a deterministic tool cannot express, and they are slower, more expensive, and non-deterministic.

Why do AI coding agents need maintainability sensors specifically?

Agents need sensors because they work without a durable mental model of your codebase, and internal quality is what lets them keep working. This is the practical argument Böckeler makes, and it matches what we see in production.

A tangled codebase hurts an agent in three concrete ways:

  • It forces the agent to load excess context to understand what a change touches, which burns tokens and crowds the window.
  • It lets the agent miss an existing implementation and write a second, slightly different one.
  • It produces inconsistent variations of the same pattern, because nothing signalled that a convention already existed.

As the share of AI-authored code rises, these effects compound. Our Q1 2026 AI Velocity Report records 84% of code as AI-authored, up from 51% the previous quarter. At that ratio, maintainability is not a tidiness concern. It is the thing that keeps the agent productive on the next change, and it is why we treat sensors as core infrastructure rather than optional polish.

Why build your own sensors instead of relying on off-the-shelf tools?

Off-the-shelf tools catch generic problems, but your codebase has specific conventions, domain rules, and failure modes that no library knows about. The highest-value sensors are usually the ones you build yourself, and AI coding has made them cheap enough to be worth it. In practice we now run far more of our own tooling as sensors than we do off-the-shelf libraries.

A linter knows about an unused variable. It does not know that your booking service must never call the pricing client directly, that your accessibility baseline is stricter than the generic rule set, or which of your queries is missing an index under realistic data. Those codebase-specific failures are the ones that actually hurt, and a generic tool cannot see them.

AI changes the economics. Writing a bespoke sensor used to be a small project, so most teams settled for what a library gave them. Now an agent can produce a targeted analyser, check, or focused test in minutes, which drops the bar for what is worth automating. The custom sensors we get the most from:

  • Accessibility sensors tuned to our component library and our Web Content Accessibility Guidelines (WCAG) targets, not just a generic rule set, so the agent cannot ship an inaccessible pattern.
  • SQL indexing and performance sensors that flag missing indexes, query-plan regressions, and N+1 access (one query per row instead of one for the set) against realistic data.
  • End-to-end Playwright tests that exercise real user journeys, so a change that passes unit tests but breaks a flow is caught before review.
  • Container-based integration tests that run against real dependencies, a real database, queue, and cache, rather than mocks, catching the integration failures agents are prone to.
  • Pattern and architecture review that flags likely pattern infringements and proposes suggestions, rather than trying to encode every pattern as a deterministic rule. The concern needs judgement, so this sensor is inferential and always paired with manual engineer review.
  • Database schema-change review where an AI sensor assesses each proposed schema or migration change and an engineer signs it off, rather than a fixed rule deciding pass or fail.

Off-the-shelf tools still earn their place as the cheap baseline. Type checkers, linters, and mutation testing are not worth rebuilding. The point is that the baseline is where you start, not where the value is. Every recurring, codebase-specific mistake is a candidate for a custom sensor. Where the concern is deterministic, we build a check that fails the build; where it needs judgement, like pattern infringements or schema changes, we build an AI sensor that flags and suggests, and a human decides. Either way, AI makes the bespoke sensor cheap to build.

Which sensors run during the coding session?

The session sensors are the fast, deterministic checks the agent runs itself before declaring a task done. They are the cheapest feedback you can give, and the most valuable, because they close the loop without a human.

For React and TypeScript:

  • The TypeScript compiler in no-emit mode (tsc --noEmit) with a strict tsconfig.
  • ESLint and Stylelint with the project rule set.
  • The unit test runner (Vitest) on the affected files.

For .NET:

  • dotnet build with warnings treated as errors.
  • dotnet test on the affected project.
  • dotnet format to settle style before review.

The agent is told to run these as a single bundle, usually a package.json script or a shell alias, so it cannot run one and skip the rest. The point is not the individual tool. It is that the agent receives a deterministic verdict in seconds and fixes the problem while the context is still loaded, rather than handing a broken change to a reviewer.

How do you enforce architecture with structural sensors?

Structural sensors answer a question single-file linting cannot: does this change respect the boundaries between layers? Basic linting catches an over-long function. It cannot see that a controller now reaches straight into the database.

On .NET, we use ArchUnitNET to write module-boundary tests. These assert rules such as “controllers may not reference the data layer directly” or “domain types may not depend on infrastructure”. The rule runs as an ordinary test, so a breach fails the build the same way every time. The agent cannot quietly leak a layer because the sensor catches it deterministically.

On React and TypeScript, dependency-cruiser plays the same role. You declare a layered architecture (routes, services, clients, domain) and forbid the edges that should not exist. Böckeler’s worked example used dependency-cruiser to enforce exactly this kind of layering, then went further with coupling analysis that surfaced “god modules” and inefficient parameter passing.

Off-the-shelf structural tools are the baseline for hard boundaries: ArchUnitNET and dependency-cruiser fail the build deterministically when a layer is breached. Many concerns resist a deterministic rule, though. For pattern infringements and suggestions, and for database schema changes, we do not try to encode a rule. We use an inferential (AI) sensor that flags likely problems and proposes fixes, paired with manual engineer review. The AI surfaces the candidates; a human makes the call.

Two practical cautions:

  • Run structural rules before deeper coupling analysis. The cheap, declarative rules catch the obvious breaches; the expensive analysis is for what remains.
  • Watch for displaced complexity. Böckeler notes that a rule forcing smaller functions can simply push complexity into component properties. A sensor that moves a problem rather than removing it is a false win.

Why does test coverage mislead on AI-written tests?

Coverage tells you a line ran, not that anything checked its behaviour. This gap matters more with agents, because they readily produce high-coverage suites whose assertions are weak or missing. The suite is green, the coverage number is high, and the tests verify almost nothing.

Mutation testing is the sensor that exposes this. It deliberately changes the code under test, for example flipping a comparison or removing a line, then reruns the tests. If no test fails, the mutant “survived”, which means a test executed that code but never actually tested it. Böckeler’s review found exactly this: high-coverage mappers whose assertions did not verify the mapping.

On .NET we run Stryker.NET for this. Incremental runs keep it affordable, so it sits in the post-integration tier rather than on every commit. The output is a list of surviving mutants, which is a precise, actionable signal: each one names a behaviour the test suite claims to cover but does not.

We cover the broader correctness question, including model-based evaluation of generated tests and policy logic, in a separate guide on mutation testing and semantic evals.

When do you need an inferential (LLM) sensor?

Reach for a model-based sensor only when the question needs semantic judgement that no deterministic tool can express. There are real cases:

  • Is this code semantically duplicating logic that already exists elsewhere?
  • Does it use our domain language, or invent new terms for existing concepts?
  • Does this change alter the architectural intent, even though it passes every structural rule?

Böckeler’s modularity review is a good example. An inferential pass identified duplicate route code and semantic inconsistencies that required changes across more than 40 files, the kind of cross-cutting design problem a computational tool will not see.

The discipline that makes inferential sensors work is grounding. Run them downstream of the computational sensors, and tell the review agent to read that output before it speculates. Without grounding, an inferential review tends to hallucinate problems and miss real ones. The heuristic from the harness guide holds: a control that produces the same answer twice should be promoted to a computational check; only genuine judgement stays inferential.

How do you give sensors agent-readable output?

A sensor is far more useful to an agent when its output says how to fix the problem, not just that something failed. Böckeler calls this a positive kind of prompt injection, and it is one of the highest-leverage details in the whole approach.

In practice this means:

  • Custom ESLint formatters that print the correction guidance alongside the warning.
  • Build and test failures that name the convention and point at the fix, not just the line.
  • Structural-test failures that state the rule in plain language (“controllers must not reference the data layer; move this call into a service”).

Pair this with a release valve. Let the agent suppress a warning or raise a threshold when it records a written justification, rather than forcing binary compliance. A rule the agent can override with a recorded reason is more honest than a rigid rule it learns to route around, and the justification leaves an audit trail a human can review later.

How should you sequence sensors across session, CI, and schedule?

Order sensors by cost, cheapest first, so the expensive checks only run on changes that already passed the cheap ones. This is the single most important configuration decision, because it keeps fast feedback fast.

A workable layering for a .NET and React system:

  • Every commit, in-session: tsc, ESLint, dotnet build, focused unit tests, dotnet format. Seconds, not minutes.
  • Every pull request, in CI: the full test suite, custom accessibility sensors, end-to-end Playwright tests, container-based integration tests, plus the off-the-shelf baseline of ArchUnitNET, dependency-cruiser, SqlPackage schema-diff, and CodeQL.
  • Scheduled or post-integration: Stryker.NET mutation testing (incremental) and SQL indexing and performance review, plus inferential reviews paired with manual sign-off for pattern infringements, suggestions, and database schema changes.

The rule is that a computational check on every commit is essentially free, while an inferential check on every commit is expensive in tokens and latency. Put the deterministic, cheap sensors in the tight loop and reserve the slow, costly ones for the cadence where they earn their keep.

How does Talk Think Do use maintainability sensors?

We run this sensor layer on client codebases as standard, and the Q1 2026 AI Velocity Report is the public record of it. The relevant points:

  • 84% of code is AI-authored, which is only safe because the sensor layer holds internal quality steady.
  • We increasingly build our own sensors rather than relying on off-the-shelf libraries: custom accessibility sensors, SQL indexing and performance sensors, end-to-end Playwright tests, and container-based integration tests. For pattern infringements, suggestions, and database schema changes, where a deterministic rule does not fit, we use an AI review paired with manual engineer sign-off. AI coding is what makes that level of specificity affordable.
  • Off-the-shelf tools provide the baseline: ArchUnitNET for module boundaries, Stryker.NET for mutation testing, and GitHub Advanced Security with CodeQL for security findings.
  • Six live custom MCP servers feed the agent real signals from work tracking, test execution, logging, Azure, CI/CD, and GitHub, so it self-corrects against the actual system rather than its own guess at it.
  • Every AI-authored change still goes through senior engineer review and ISTQB-qualified QA validation, inside our ISO 27001-certified framework.

Sensors do not remove the human. Böckeler is explicit that they are not a complete solution, and our experience agrees: they substantially raise review confidence and catch issues earlier, but judgement still sits with a qualified engineer. The economics of 84% AI-authored code depend on that division of labour.

If you are adding sensors to an existing codebase, the order that works is the same as for the wider harness: fast computational sensors first, then structural tests, then mutation testing, then the grounded inferential review last. For the feedforward half of the picture, read the harness engineering guide and the harness templates guide. For the day-to-day .NET tooling, see the Claude Code for .NET developers guide. To weigh the wider trade-offs, see the risks of AI-augmented development.

To talk through a sensor review of your own codebase, book a free consultation.

Frequently asked questions

What is a maintainability sensor?
A maintainability sensor is an automated check that observes what a coding agent did and signals whether the change keeps the codebase healthy. Sensors run at four points: during the coding session, in continuous integration, on a schedule, and in production. They split into computational sensors (deterministic tools like type checkers, linters, and structural tests) and inferential sensors (model-based reviews that need semantic judgement).
Why do AI coding agents need maintainability sensors more than human developers?
Agents work without a persistent mental model of the codebase. A tangled codebase forces an agent to load excess context, miss an existing implementation, and add inconsistent variations of the same logic. Sensors give the agent fast, repeatable feedback so it self-corrects before a human reviews the change, which keeps internal quality from eroding as the share of AI-authored code rises.
Does high test coverage mean AI-generated tests are good?
No. Coverage only proves a line was executed, not that an assertion verified its behaviour. AI agents often produce high-coverage suites whose assertions are weak or absent. Mutation testing, for example Stryker.NET on the .NET side, deliberately changes the code and checks whether a test fails. A surviving mutant means a test executed the code but did not actually test it.
When should you use an LLM-based sensor instead of a deterministic one?
Use an inferential, model-based sensor only for questions a deterministic tool cannot answer: whether code semantically duplicates existing logic, whether it matches your domain language, or whether a change alters architectural intent. Run it downstream of the computational sensors and tell it to ground its findings in their output. Anything that produces the same answer twice should be a computational check instead.
Which maintainability sensors should a .NET and React team start with?
Start with the fast computational sensors that run on every commit: tsc, ESLint, and the test runner for React and TypeScript, plus dotnet build, dotnet test, and dotnet format for .NET. Add ArchUnitNET and dependency-cruiser for architecture boundaries, then Stryker.NET mutation testing post-integration. Add a grounded inferential review agent last, once the computational layer is reliable.
Should you use off-the-shelf sensors or build your own?
Use off-the-shelf tools as the cheap baseline: type checkers, linters, and mutation testing. Then build custom sensors for the failures specific to your codebase, such as accessibility, SQL indexing and performance, end-to-end journeys, and container-based integration. Where a concern needs judgement, like pattern infringements or database schema changes, use an AI review paired with manual sign-off rather than a deterministic rule. AI coding makes bespoke sensors cheap to build.
How do you stop sensors from blocking the agent unnecessarily?
Let the agent suppress a warning or raise a threshold when it gives a written justification, rather than forcing binary compliance. A rigid rule that pushes complexity elsewhere is worse than a rule the agent can override with a recorded reason. Pair this with agent-readable messages that say how to fix the issue, not just that it failed.

Ready to transform your software?

Let's talk about your project. Contact us for a free consultation and see how we can deliver a business-critical solution at startup speed.