Rules Overview¶
Algorilla ships with 28 built-in rules that detect common algorithmic complexity anti-patterns, organized in six categories.
Loop amplifiers¶
Expensive operations hidden inside loop bodies that multiply the cost per iteration.
| Rule | Impact |
|---|---|
| Nested Lookup | Linear search in a loop → quadratic cost. Fix: use a Set |
| Repeated Linear Scan | Same collection scanned multiple times → combine into one pass |
| Repeated Regex in Loop | Regex compiled every iteration → compile once before the loop |
| Regex Recompilation in Loop | Regex literal recompiled per call → hoist to a constant |
| Expensive Serialization in Loop | Serializer created per iteration → reuse a single instance |
| Sequential Async Join in Loop | Awaits one-by-one in a loop → launch all, await together |
| In-Loop Collection Building | Collection copied every iteration → build once outside the loop |
| String Concat in Loop | String copied every iteration → use StringBuilder |
| Quadratic Removal | Removing from middle of a list shifts all elements → use an iterator or filter |
| Repeated Reflection in Loop | Reflection lookup per iteration → cache the Method/Field |
| Hidden Nested Loop | Method call hides a nested loop → O(n²) invisible at call site |
| IO In Loop | Network/DB/file call per iteration → batch or parallelize |
| Expensive Callback | Expensive operation inside a callback (filter, map, sort) |
| Cardinality Explosion | Cross-product output grows as O(n×m) |
Sort abuse¶
Wasteful sort operations or expensive work inside sort comparators.
| Rule | Impact |
|---|---|
| Sort for Last | Sorting entire collection just to get min/max → use min()/max() |
| Expensive Sort Comparator | Expensive computation inside comparator runs O(n log n) times → precompute |
| Filter After Sort | Sort runs before filter → filter first, sort fewer elements |
Query patterns¶
Database and service call patterns that cause excessive I/O.
| Rule | Impact |
|---|---|
| N+1 Query | DB query per item in a loop → single bulk query |
| Bulk Load for Single Lookup | Loads all records to find one → query for just the one |
| Lazy Loading In Loop | ORM lazy-loads per iteration → eager fetch or batch |
Construction cost¶
Unnecessary object creation overhead.
| Rule | Impact |
|---|---|
| Expensive Construction | Heavyweight object created per iteration → reuse a single instance |
Redundancy¶
Repeated computations that could be cached.
| Rule | Impact |
|---|---|
| Redundant Expensive Call | Same expensive call made multiple times → cache in a local variable |
| Uncached Getter | Same getter called repeatedly → cache the result |
| Chained Getters | Deep getter chain traversed multiple times → flatten or cache |
| Unmemoized Recursion | Recursive function recomputes same inputs → add memoization |
| Repeated Collection Iteration | Same collection iterated multiple times → combine into one pass |
| Loop-Invariant Hoisting | Same call repeated every iteration with same result → hoist before the loop |
Concurrency¶
Patterns that undermine parallel execution performance.
| Rule | Impact |
|---|---|
| Parallel Pipeline Bottleneck | Synchronization bottleneck in parallel pipeline |
Disabling Rules¶
In .algorilla.yml:
Or suppress inline:
// algorilla:ignore nested-lookup
for (Order order : orders) {
if (discountedProductIds.contains(order.getProductId())) { ... }
}
Have questions about how rules work, what the confidence levels mean, or how Algorilla compares to other tools? Check the FAQ.