Good to see you again. In the previous lesson, you turned an approved implementation plan into dependency-aware task contracts with explicit integration checkpoints. That work gives a code reviewer something much better than a raw diff: intended behavior, constraints, ownership, rollout conditions, and evidence that should exist when the change is ready.
This lesson completes the module by turning those artifacts into a risk-based AI-assisted code review process. The aim is not to read every AI-produced line with equal suspicion. It is to concentrate human attention and verification effort where a defect could violate a business invariant, expose data, create an incident, or make an incident difficult to diagnose and reverse.
A checklist is a prioritization system, not a ritual
AI can generate large, plausible-looking diffs quickly. That changes the bottleneck: review capacity is now scarcer than code-generation capacity. An undifferentiated checklist does not solve this problem. If every pull request receives fifty equally weighted questions, reviewers either rubber-stamp the list or spend disproportionate time on low-consequence details.
A risk-based review starts with four questions:
- What can go wrong? Identify violated requirements, invalid state, data exposure, excess resource use, and unrecoverable operational behavior.
- How severe is it? Consider customer impact, integrity loss, security exposure, financial impact, and blast radius.
- How plausible is it? Look for an actual code path, a reachable input, a realistic failure mode, or an absent control.
- How would we know and recover? Ask whether tests, traces, metrics, logs, flags, and rollback mechanisms would expose and contain the failure.
This is deliberately qualitative. A public, multi-tenant write endpoint that changes authorization, persistence, and a Kubernetes deployment deserves much deeper review than a localized documentation fix. The fact that both diffs may contain the same number of lines is irrelevant.
A useful starting classification is:
| Review level | Use when | Expected evidence |
|---|---|---|
| Focused | Low blast radius, easily reversible change, stable behavior, no trust-boundary change | Targeted tests, deterministic checks, brief human diff review |
| Standard | Typical endpoint, service, data-access, or integration change | Requirement traceability, behavior tests, AI review findings validated by a human |
| Deep | Auth, permissions, secrets, payment or workflow state, migrations, shared libraries, production infrastructure, high-throughput paths | Threat/data-flow analysis, integration evidence, operational and recovery review, domain-owner review where needed |
Feature flags reduce release exposure, but they do not make a defect harmless. For example, a disabled-by-default cache feature still needs proof that enabling it cannot cross tenant boundaries, saturate Redis, or remove visibility into Elasticsearch latency.
Before looking at implementation details, make a compact review brief:
Intent:
- What user or system behavior should change?
Constraints:
- Which architecture, authorization, data, testing, observability,
rollout, and rollback requirements are non-negotiable?
Change map:
- Entry points, changed components, external calls, persistence,
configuration, and deployment artifacts.
Risk triggers:
- Trust boundary? Sensitive data? State transition? Migration?
Hot path? New dependency? Kubernetes or secret change?
Evidence available:
- Tests run, benchmark or load evidence, dashboard links,
traces/logs, migration plan, feature-flag and rollback behavior.
Unknowns:
- What must be verified rather than assumed?
This is the review counterpart to the evidence-first context packet and task contracts from earlier lessons. It prevents an AI reviewer from treating unfamiliar repository conventions as facts, and it keeps a human reviewer from judging code merely by whether it “looks idiomatic.”
Build a review pipeline: deterministic checks first, judgment second
An effective workflow separates checks that can be proven mechanically from questions requiring system and domain judgment.
How I Review AI-Generated Code
Watch “How I Review AI-Generated Code” by Owain Lewis for a practical layered workflow: deterministic checks remove routine defects, then AI and human reviewers focus on higher-value reasoning.
Watch deterministic checks for the distinction between repeatable gates—formatting, type checking, tests, linting, and security scanning—and review judgment. Then watch the local AI pass, focusing on the recommendation to inspect the diff and run the application before delegating a structured review to an AI agent. Treat the named tools as examples, not requirements.
A practical pipeline for your .NET and Node.js repositories has four layers:
-
Local deterministic validation. Run formatter, compiler or type checker, unit tests, dependency checks, relevant static analysis, and repository-specific validation commands. An AI agent can run these and fix failures, but a passing command only proves the property that command checks.
-
AI-assisted risk passes. Give an AI reviewer the review brief, changed files, relevant tests, and a narrow assignment such as “trace authorization and tenant identity” or “review cancellation, retries, and bounded resource use.” Separate passes are generally more useful than one vague request to “review this code.”
-
Automated pull-request checks. CI repeats deterministic validation in a clean environment and can run a separate AI review or security-focused scan. This provides independence from the authoring agent and from local machine state.
-
Human decision and accountability. A human validates material findings, resolves ambiguity in requirements, judges business behavior, and decides whether risks are acceptable. This is especially important for migrations, authorization, infrastructure, and incident-prone paths.
The core rule is:
AI output is a set of review hypotheses, not evidence that a defect exists.
For each AI finding, ask: What exact line or behavior is at risk? What input or failure condition reaches it? What requirement does it violate? What test, documentation, type definition, runtime experiment, or trace would confirm or refute the claim?
That discipline avoids two opposite errors: blindly accepting confident but incorrect model feedback, and ignoring a real problem because the model’s first explanation was imprecise.
A useful AI-review contract
For a deep or standard review, use an explicit request rather than a generic “find bugs” prompt:
Review this change against the supplied review brief.
Scope:
- Review only the stated diff and the named surrounding code.
- Do not invent repository behavior, API capabilities, owners, or thresholds.
- State uncertainty where evidence is missing.
Perform four passes:
1. Correctness: requirements, invariants, errors, boundary cases,
concurrency, cancellation, and data integrity.
2. Security: trust boundaries, authorization, injection, secrets,
logging, dependencies, and unsafe configuration.
3. Performance: query fan-out, algorithmic growth, allocations,
external calls, timeouts, retries, concurrency, and backpressure.
4. Operability: configuration, deployment, telemetry, alerts,
rollback, feature flags, and failure behavior.
For each finding, return:
- severity: blocker, must-fix, follow-up, or question
- exact location
- concrete failure scenario
- violated requirement or expected property
- evidence supporting the finding
- minimal correction or verification step
Do not report style preferences as blockers.
A separate review agent is useful, but “separate” should mean more than asking the same agent that wrote the code to declare it correct. Give the reviewer a distinct role, a clean task, relevant repository context, and permission to report uncertainty.
Pass 1: correctness means preserving intended behavior under real conditions
Correctness review begins with the intended behavior, not the implementation technique. Read the pull request description, approved plan, acceptance criteria, and relevant task card before reading the diff. Then trace a few representative executions:
- a normal successful request;
- empty, missing, malformed, and boundary inputs;
- an expected dependency failure;
- a concurrent or repeated request where applicable;
- the existing behavior when a flag is disabled or a new configuration is absent.
The key question is not “does this compile?” It is:
Can I explain why this code preserves the relevant invariant across success, failure, and boundary paths?
For web services, the most productive correctness technique is usually data-flow and state-flow tracing. Start at an HTTP handler, message consumer, cron job, or tool invocation. Follow request values through validation, normalization, authorization, domain logic, persistence, caching, external calls, logging, and the response. At every boundary, ask what values are assumed to be present, valid, current, and owned by the caller.
The following review cues are high-yield in both C# and TypeScript:
| Area | Questions that expose defects |
|---|---|
| Null and defaults | Does absent differ from empty, zero, or false? Does a default silently broaden behavior? |
| Boundaries | What happens at empty input, first/last page, maximum size, duplicate item, expired record, and unsupported enum value? |
| Time | Is stored time unambiguous? Is “now” injectable in tests? Are expiry, time zones, and daylight-saving behavior deliberate? |
| State and idempotency | Are allowed transitions explicit? Can retries duplicate a write, notification, billing action, or ticket update? |
| Concurrency | Does a check and subsequent action need to be atomic? Can requests race on shared state or cached values? |
| Errors | Are external failures translated consistently? Are errors swallowed, incorrectly retried, or exposed as false success? |
| Data integrity | Do application assumptions match database constraints, migration behavior, serialization, and schema evolution? |
Read the selected parts of “Code Review Checklist and Anti-Pattern Catalog” from hidekazu-konishi.com as a compact set of review prompts for bug risk, performance, and AI-specific failure modes. Use it to sharpen what you inspect; do not turn every item into a mandatory blocker.
In “Checklist 1: Bug Risk Indicators,” read the bug-risk checklist. Pay particular attention to state transitions, resource ownership, cancellation propagation, and error paths. Then read “Checklist 3: Performance and Resource Usage,” beginning with the performance pass. Apply its “100x load” question to changed loops, Elasticsearch queries, external calls, and logging. Finally, in “AI-Specific Risks,” read the AI-specific review section. Focus on checking API and package reality, avoiding speculative abstractions, and requiring rationale for non-obvious decisions.
Tests are evidence, but inspect what they prove
AI-generated tests can create a particularly convincing form of false confidence: many passing tests that mirror the implementation rather than enforce the requirement.
When reviewing tests, ask:
- Would this test fail if the requirement were violated?
- Does it test the externally observable result, not a private implementation detail?
- Does it cover the error or rejection path introduced by the diff?
- At integration seams, does it exercise a real contract or only a model-authored mock?
- Does a regression test identify the invariant or defect it protects?
For a newly added external SDK call, a mocked unit test proves only that your code agrees with the mock. It does not prove the method name, parameter shape, response schema, or retry semantics are supported by the real SDK or API version. Type resolution, official documentation, and a contract or integration test provide stronger evidence.
Pass 2: security review follows trust boundaries and assets
Security review is most efficient when it follows assets and flows, rather than scanning for a memorized list of dangerous strings.
For each changed entry point, identify:
- Source: user request, queue message, file, environment variable, database record, third-party API, or retrieved document.
- Trust boundary: where data moves from a less trusted context into a more trusted one.
- Transformation: parsing, validation, normalization, authorization, encryption, query building, or deserialization.
- Sink: database query, Elasticsearch query, filesystem access, browser output, log event, external API, shell command, or privileged action.
Authorization deserves special attention in multi-tenant services. Authentication establishes an identity; authorization establishes whether that identity may perform this action on this resource. A route value such as tenantId, accountId, or userId is an input, not proof of entitlement. The review should locate the server-side decision that relates the authenticated principal to the requested resource.
Secure Code Review - OWASP Cheat Sheet Series
Read the OWASP Cheat Sheet Series’ “Secure Code Review Cheat Sheet” for a threat-oriented method and concrete security checks. It is particularly useful for deciding which security questions belong in a diff review rather than relying on an undirected scan.
In “Review Methodology,” start in “Preparation” and read the diff-based review guidance. Notice the focus on altered trust boundaries, security-control impact, and new integrations. Then scan the checklists from threat modeling through monitoring. In “Input Validation,” “Authorization,” “Business Logic,” “Configuration & Deployment,” and “Security Monitoring,” select the items that match this pull request’s actual attack surface.
For everyday API and service changes, this compact security checklist covers the highest-value questions:
| Risk area | Review questions |
|---|---|
| Input and parsing | Is all untrusted input validated server-side? Are allowlists, bounds, and size limits applied before expensive work? Is parsing delegated to a trusted library? |
| Injection and output | Is data parameterized or encoded for the specific target grammar: SQL, Elasticsearch query DSL, HTML, URL, shell, or log format? |
| Authorization | Is access enforced server-side for the specific object and action? Does default behavior deny access? Can direct API calls bypass UI-only restrictions? |
| Sensitive data | Are secrets absent from source, fixtures, screenshots, configuration, exception messages, and logs? Are personal or regulated fields redacted where necessary? |
| Dependencies | Does every new package exist, match official documentation, and appear in the lockfile as expected? Is the version and transitive dependency change understood? |
| Configuration | Are secure defaults maintained across environments? Is TLS, secret injection, error behavior, and environment separation appropriate? |
| Auditability | Can an operator reconstruct sensitive actions and authorization decisions without logging credentials or full tokens? |
AI-generated code adds several special failure modes:
- A package name, method, option, or configuration key can be plausible but nonexistent.
- A generic implementation can silently replace a domain-specific rule with a common but incorrect default.
- A new abstraction can hide a simple authorization or data-flow decision behind unnecessary indirection.
- Large, repetitive generated blocks can exhaust reviewer attention and conceal one inconsistent branch.
- A non-obvious choice may lack any rationale because the model generated a statistically common pattern rather than an intentional design.
Verify imported packages against the actual registry and lockfile. Resolve unfamiliar methods against the installed version’s type definitions or official documentation. For security-sensitive logic, require a human who can explain why the control is correct for this system—not simply that a security-oriented agent approved it.
Pass 3: performance review asks what happens at production scale
Performance review is not an instruction to optimize every allocation. It is a search for nonlinear growth, unbounded work, and resource contention that normal test data does not reveal.
Use the “100x” thought experiment:
At one hundred times the current request rate, payload size, result count, or concurrent user count, what becomes qualitatively different?
For .NET and Node.js application changes, inspect these areas first:
- Query shape: Does a loop issue one database or Elasticsearch request per item? Are filters selective and pagination bounded? Does a query retrieve far more source data than the endpoint needs?
- Algorithmic growth: Did an innocent nested loop change work from proportional to input size into quadratic work?
- Memory: Is a large response read fully into memory when it could stream? Can a new cache grow without a size cap, TTL, or eviction policy?
- External calls: Do HTTP, Redis, Elasticsearch, database, and model calls have timeouts and cancellation? Are retries bounded, backoff-aware, and limited to safe operations?
- Concurrency: Does
Task.WhenAllorPromise.allcreate unbounded parallelism over user-controlled input? Is backpressure present? - Hot-path logging: Could a debug-shaped log event become a high-cardinality or high-volume production cost?
In C#, watch for sync-over-async patterns such as .Result or .Wait() in request paths, lost CancellationToken propagation, and unbounded fan-out. In Node.js, inspect CPU-heavy synchronous work on the event loop, unbounded promise creation, stream handling, and whether request-abort signals reach outbound operations.
Performance findings should be tied to an expected load or a measurable resource. “This might be slow” is not actionable. “The endpoint now performs one Elasticsearch query per authorization scope; a user can have hundreds of scopes, so the request count grows with scope count and has no concurrency limit” is a testable review hypothesis.
Pass 4: operability asks whether the change can be run, diagnosed, and contained
A service can be functionally correct and still be unsafe to operate. Operability review asks what happens when dependencies slow down, configuration is wrong, traffic spikes, a rollout partially fails, or an on-call engineer has only telemetry and a feature flag.

For a changed endpoint or background workflow, check that an operator can answer:
- What happened? Correlated request or trace ID, meaningful error category, and enough endpoint or operation context.
- How often and how badly? Request rate, error rate, latency distribution, saturation or queue depth where applicable, and dependency-call outcomes.
- Which dependency failed? Separate timing and failure information for Elasticsearch, Redis, databases, HTTP services, or model APIs rather than one opaque “request failed” metric.
- Can we limit harm? Timeouts, cancellation, rate limits, circuit behavior where appropriate, and bounded retries.
- Can we recover safely? Feature flag, rollout control, rollback-compatible migration, documented configuration behavior, and an owner for release decisions.
A dashboard with a request count is not enough. Consider the cache feature from the preceding lessons. If aggregate endpoint latency falls while Elasticsearch errors become hidden by cache hits, the service can look healthier than it is. Useful telemetry distinguishes:
- cache hit, miss, bypass, and cache failure;
- elapsed time in the cache and in Elasticsearch;
- fallback behavior;
- tenant-safe dimensions that do not create uncontrolled metric-cardinality;
- feature-flag state or rollout cohort, where privacy policy permits.
Operational review also includes configuration and deployment artifacts. For Kubernetes changes, inspect resource requests and limits, readiness and liveness semantics, secret references, default values, network dependencies, autoscaling assumptions, and failure behavior during rolling deployment. A YAML file that applies successfully is not necessarily safe under load or during an unavailable dependency.
A worked review: AI-generated search-cache change
Assume an AI agent has prepared a pull request that adds a feature-flagged Redis response cache to an ASP.NET Core search endpoint backed by Elasticsearch. The stated objective is lower latency for repeated authorized searches while preserving tenant isolation and allowing immediate disablement.
This is a deep review. The change is on a hot path, handles tenant-scoped results, introduces an external dependency, changes latency behavior, and requires safe rollout.
A concise review map might look like this:
| Axis | High-risk question | Evidence to seek |
|---|---|---|
| Correctness | Does the key represent the complete request semantics, including normalized filters, relevant policy version, and freshness behavior? | Unit tests for equivalent and non-equivalent requests; explicit expiry behavior |
| Security | Is the key based on the authorization decision or trustworthy identity context, rather than only a caller-controlled route value? | Server-side authorization flow; cross-tenant and cross-role tests |
| Performance | Can cache calls block request capacity, retry in a storm, or trigger concurrent cache-miss fan-out? | Timeout and cancellation propagation; bounded retry policy; concurrency/load evidence |
| Operability | Can on-call distinguish cache behavior from Elasticsearch behavior and safely disable the feature? | Metrics, traces, dashboard query, flag default, fallback test, rollback notes |
Now imagine an AI reviewer reports: “The cache key does not include the caller’s tenant.”
Do not accept or dismiss the comment on wording alone. Verify it:
- Find where the cache key is constructed.
- Trace every component of that key to its source.
- Locate the server-side authorization decision.
- Determine whether the route tenant, authenticated tenant claim, organization membership, and authorization scope can differ.
- Run or add a test in which two authorized identities make otherwise identical requests but must receive different result sets.
If inspection confirms that a caller-controlled route value can select a shared cache entry without matching an authorization-derived scope, that is a blocker. It has a credible cross-tenant data-disclosure path. If the authorization-derived tenant and complete authorization scope are already part of a centrally generated key, the AI finding is false or incomplete; record why, and improve the test if the evidence was difficult to locate.
This distinction matters because good review comments communicate evidence and consequence:
Blocker — cross-tenant cache isolation: The key is built from normalized query fields and the route tenant, but this handler authorizes access using organization membership after key lookup. If a caller can supply a tenant route value that does not match their effective organization scope, an existing cached result may be returned before object-level authorization. Construct the key from the resolved authorization scope, or authorize before lookup, and add a test proving two identities with identical query parameters cannot share a cache entry.
Compare that with a non-blocking suggestion:
Suggestion — cache telemetry: The new
search.cache.resultmetric records hit and miss but not cache-failure fallback. Adding a bounded result label for fallback would help distinguish cache availability problems from normal misses during rollout.
The first identifies a reachable violation and required proof. The second improves diagnosis but does not claim the current behavior is unsafe.
Make review outcomes explicit
AI tools often produce many comments. A review process needs a consistent way to decide what blocks a merge.
| Outcome | Meaning | Expected action |
|---|---|---|
| Blocker | Credible path to security exposure, incorrect behavior, data loss/corruption, major availability risk, or violated hard requirement | Fix and provide verifying evidence before merge |
| Must-fix | Material defect or operational gap likely to cause unacceptable support or incident risk | Fix before merge or obtain an explicit, accountable exception |
| Follow-up | Real improvement with bounded current risk | Create an owned task with rationale and a target milestone |
| Question | Evidence is insufficient to determine whether behavior is correct | Resolve through documentation, a test, source inspection, or an experiment |
| Nit | Non-blocking style or readability preference | Do not use to delay a correct change |
Avoid turning adjacent technical debt into a reason to block a scoped pull request. If it is genuinely out of scope, create a separate task. Conversely, do not downgrade a security or correctness defect merely because it appears in pre-existing code: if the new change depends on or expands that unsafe behavior, it is part of the review risk.
Before approving an AI-assisted change, produce a short review decision record:
Decision: approve / approve with follow-up / request changes
Confirmed evidence:
- Commands and tests run
- Integration or contract evidence
- Reviewed operational signals and rollback path
Resolved findings:
- Finding ID, severity, correction, and proof
Accepted follow-ups:
- Owner, due condition, and reason merge is safe now
Open assumptions:
- What remains unverified and who will verify it
This record is lightweight, but it prevents “the agent said it was fine” from becoming the undocumented reason a change was merged.
Key takeaways
A risk-based AI-assisted code review is a controlled evidence process, not a contest between human and model judgment.
- Scale review depth by blast radius, trust boundaries, reversibility, uncertainty, and operational impact, not diff size.
- Use deterministic checks to remove mechanical failures, then use AI for targeted hypothesis generation and humans for validation and accountable decisions.
- Review correctness by tracing requirements, state, data, failure paths, concurrency, and tests that prove behavior.
- Review security at trust boundaries, especially server-side authorization, input handling, secrets, dependencies, and auditability.
- Review performance by looking for unbounded work, nonlinear query or algorithmic growth, uncontrolled concurrency, and missing timeouts or cancellation.
- Review operability by requiring diagnosable telemetry, safe configuration, controlled rollout, and a tested recovery path.
- Treat AI findings as claims that need evidence. A precise, reproducible blocker is valuable; an unsupported warning is a question to investigate, not a verdict.
This completes High-Leverage AI Engineering Workflows. Next, the course shifts beneath the tooling layer to how language models behave, beginning with tokenization and its effects on code representation, context limits, and API cost.
Can't find a good explanation? Sign up and we'll make it for you
Sign up