How Code Evolution Lab turns a detected issue into concrete, runnable fixes.
This applies to web and API scans only. The CLI and GitHub Action detect issues and report them, but do not generate fixes.
What it does
Most linters tell you a line is wrong. Code Evolution Lab attempts to rewrite it, producing several candidate fixes derived from your actual code, each scored and ranked.
Generation runs in two phases. The heuristic phase applies transformation strategies to produce an initial set of solutions. Those are then deduplicated — near-identical candidates that differ only by variable naming are collapsed — ranked, and capped to your tier’s solutions-per-issue limit.
Some issues then go through a second phase, where those solutions become the
starting population for evolution.
Not every issue qualifies: evolution runs only for issues of type
n_plus_1_query, nested_loops, nested_array_methods or await_in_loop, or
for any issue of high or critical severity. Everything else stops after the
heuristic phase. If evolution is disabled, times out, or fails, the heuristic
solutions are what you get.
Solutions come from generators written in one of two styles, and it is worth knowing which you are looking at:
- Transformation-based generators parse your actual code and build a fix
seeded with your real variable names, ORM and loop structure.
N1SolutionGeneratorandMissingIndexSolutionGeneratorwork this way. - Template-based generators emit worked before/after examples showing the
correct pattern, which you adapt to your code.
MemoryLeakSolutionGeneratorandLargePayloadSolutionGeneratorwork this way.
Transformation-based solutions record which of your original elements they preserved, and that record drives the risk rating. Template-based solutions are illustrative, so read them as guidance rather than as a patch.
Some generators are framework-aware. Detectors record a framework on the issue
(estimatedImpact.metrics.framework), and generators branch on it — a missing
listener cleanup is offered as a useEffect return in React, ngOnDestroy in
Angular, and beforeUnmount in Vue. Where no framework is recorded, the
framework-agnostic strategy is offered instead.
Generators
Each issue type is routed to a generator that knows how to fix that class of problem. There are eleven, one per detector category:
| Generator | Handles |
|---|---|
N1SolutionGenerator |
N+1 queries |
InefficientLoopSolutionGenerator |
12 loop anti-patterns |
MemoryLeakSolutionGenerator |
Event listener, timer, global, and closure leaks |
LargePayloadSolutionGenerator |
Oversized API and query payloads |
MissingIndexSolutionGenerator |
Missing and suboptimal database indexes |
MissingCachingSolutionGenerator |
Repeated calls, missing memoization |
ResourceLeaksSolutionGenerator |
Unclosed connections, streams, file handles |
BlockingIoSolutionGenerator |
Synchronous file, crypto, process, and DB calls |
DomManipulationSolutionGenerator |
Layout thrashing, unsafe innerHTML |
BundleSizeSolutionGenerator |
Heavy imports, tree-shaking opportunities |
RedosSolutionGenerator |
Catastrophic backtracking patterns |
A generic generator handles anything unrouted. The routing map lives in
backend/src/analyzer/code-analyzer.ts — an issue type absent from it will not
reach a generator, so adding a new issue type means adding a routing entry.
How a solution is built
Every generator inherits the same pipeline from BaseSolutionGenerator:
-
Analyse the original. The problematic code is parsed and reduced to a
CodePatterndescribing what shape it is — a loop with calls, chained methods, repeated access, nested loops — along with the loop variable, the collection being iterated, and the repeated calls found inside. This applies to transformation-based generators; template-based ones route on issue type alone. -
Apply strategies. Each of the generator’s transformation strategies is applied to the original code. A strategy that throws, or that returns code failing validation, is skipped rather than failing the run.
-
Apply generic transformations. A second pass runs the shared transformation candidates from
code-transformer, adding any whose transformation type isn’t already represented. These are scored at 70 plus 5 per preserved element, capped at 90. -
Validate. Generated code must contain more than comments and must show some code-like content. Anything else is rejected silently.
-
Rank and rate. Solutions are ordered, given a risk level and an estimated implementation time.
If the issue carries no original code, generation returns nothing — there is no template fallback.
Validation
Validation is deliberately shallow: it strips comments, requires something to remain, and requires that remainder to look like code rather than prose.
This catches the common failure — a strategy emitting an explanatory comment instead of a fix — but it does not verify that a solution is semantically equivalent to your original. Generated solutions are proposals, not verified refactors. Review and test them as you would any change.
Risk and time
Risk is derived from how much of your original code survived the transformation:
| Risk | Preserved elements |
|---|---|
| Low | 5 or more |
| Medium | 2 to 4 |
| High | Fewer than 2 |
Implementation time is estimated in minutes by the fitness calculator from the solution’s code and transformation type. Both are heuristics meant for prioritising among solutions, not commitments.
What a solution carries
interface Solution {
id: string;
issueId: string;
rank: number; // 1 = best
type: string; // Transformation strategy name
code: string;
fitnessScore: number; // 0-100
reasoning: string;
description?: string;
explanation?: string;
generationMethod?: 'heuristic' | 'evolutionary';
generationsUsed?: number; // Evolutionary only
implementationTime: number; // Estimated minutes
riskLevel: 'low' | 'medium' | 'high';
}
generationMethod tells you which phase produced it. Heuristic solutions carry
the strategy’s own fitness value; evolved solutions carry a score computed by
the fitness calculator.
Example transformations
These illustrate the kinds of rewrite generators produce. Actual output depends on your code, the detected pattern, and which strategies apply.
N+1 query — batch before the loop:
// Before
const orders = await Order.findAll();
for (const order of orders) {
order.user = await User.findByPk(order.userId);
}
// After
const orders = await Order.findAll();
const users = await User.findAll({ where: { id: orders.map(o => o.userId) } });
const userMap = new Map(users.map(u => [u.id, u]));
for (const order of orders) {
order.user = userMap.get(order.userId);
}
Sequential awaits in a loop:
// Before
const results = [];
for (const item of items) {
results.push(await process(item));
}
// After
const results = await Promise.all(items.map(item => process(item)));
Note that the parallel version changes concurrency behaviour. If process
hits a rate-limited API or a connection pool, a batched variant that caps
concurrency is the safer fix — which is why generators offer several
candidates rather than one.
Missing cleanup in a React effect:
// Before
useEffect(() => {
const handler = () => {};
window.addEventListener('resize', handler);
}, []);
// After
useEffect(() => {
const handler = () => {};
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
Writing a strategy
A strategy is an object with a name, a description, a fitness value, and an
apply function receiving the original code, its analysed pattern, and the
analysis context:
const myStrategy: TransformationStrategy = {
name: 'my-custom-strategy',
description: 'Applies a project-specific optimization',
fitness: 80,
apply: (originalCode, pattern, context) => ({
success: true,
code: transform(originalCode),
description: 'Applied custom optimization',
transformationType: 'my-custom-strategy',
preservedElements: ['users', 'orderId'],
}),
};
preservedElements is not cosmetic — it determines the solution’s risk rating,
so list the identifiers and structures the transformation genuinely kept.
Pass strategies to generateTransformationBasedSolutions from your generator’s
generateSolutions implementation.
Next steps
- Detectors and Solution Generators: A Field Guide — every issue type with worked examples and the strategies offered for each
- Evolutionary Algorithm
- Extending Solution Generators