Adding Framework Overlays¶
Framework overlays teach Algorilla about framework-specific methods and types, so it doesn't flag them incorrectly. This is one of the most accessible contributions — it's just YAML, no Kotlin or parser knowledge required.
When to add an overlay¶
You scanned a project that uses a framework and got false positives. Maybe Algorilla flagged repository.findById() as an expensive callback, or it warned about Assert.notNull() being called redundantly. These are legitimate framework calls that Algorilla doesn't know about yet.
The easiest way to start is from actual false positives — scan a project, see what's wrong, and add those methods. No need to catalog an entire framework in one go.
The three categories¶
Each overlay file has three sections. The key question for each method or type is: what happens at runtime?
cheap-methods¶
Methods where the call itself is not an expensive operation. They may do I/O or real work, but they're expected to be called in loops and shouldn't trigger rules like expensive-callback or redundant-expensive-call.
The test: "If I saw this called inside a forEach, would I think twice?" If not, it's cheap.
cheap-methods:
# These are normal API calls — not free, but not expensive enough to flag
- findById # single DB lookup, expected usage
- getBean # DI container lookup, cached
- hasRole # in-memory check
Cheap does not mean free. findById() hits a database — but flagging every repository call inside a loop would drown the developer in noise. The rule that matters there is n-plus-one-query, not expensive-callback.
trivial-methods¶
A stricter subset of cheap. These are effectively free — pure utility functions, null checks, string helpers, assertions. They get excluded from more rules than cheap methods do (including redundant-expensive-call, since calling StringUtils.hasText() twice with the same input is harmless).
The test: "Would anyone ever care that this was called twice?" If not, it's trivial.
trivial-methods:
# These are so lightweight that even redundant calls don't matter
- hasText # string null/empty check
- notNull # assertion
- nullSafeEquals # utility comparison
When in doubt between cheap and trivial, pick cheap. It's the safer classification — trivial suppresses more rules.
heavyweight-types¶
Types where construction is genuinely expensive. Creating these inside a loop is almost always a bug — they open connections, build pools, load configuration, or allocate significant resources.
The test: "Would I be alarmed seeing new X() inside a for-loop?" If yes, it's heavyweight.
heavyweight-types:
- ApplicationContext # loads entire Spring context
- RestTemplate # creates HTTP client with connection pool
- WebSocket # opens network connection
Most frameworks only have a handful of heavyweight types. Don't force it — if a framework has none, leave the section out.
io-methods¶
Methods that perform I/O — HTTP calls, database queries, file operations. These are relevant for rules like n-plus-one-query that detect I/O inside loops. Unlike cheap methods (which suppress findings), I/O methods enable detection.
The test: "Does this method hit the network, disk, or a database?" If yes, it's an I/O method.
io-methods:
# Each call is a database round-trip
- save
- saveAll
- deleteById
# Each call is an HTTP request
- getForObject
- exchange
- block # triggers reactive subscription
Not every framework has I/O methods. Utility libraries (Guava, Lodash) don't. Persistence and HTTP frameworks do.
The workflow¶
1. Run a scan and collect false positives¶
Scan a real project that uses the framework:
Go through the findings. For each one, ask: "Is this a real performance concern, or is Algorilla misunderstanding a framework method?" Write down the false positives — the method name and which rule flagged it.
2. Create the overlay file¶
Create core/src/main/resources/semantics/frameworks/<framework>.yml:
# Micronaut — framework overlay for Java
# DI, HTTP client, bean introspection
language: java
methods: {}
cheap-methods:
# HTTP Client
- retrieve
- exchange
- toBlocking
# Bean introspection
- getBean
- containsBean
- findBean
trivial-methods:
# Argument validation
- requireNonNull
- requirePresent
heavyweight-types:
- ApplicationContext
- EmbeddedServer
Group methods by subsystem using comments — it makes the file navigable and helps reviewers understand the reasoning.
3. Register it¶
Append the filename to core/src/main/resources/semantics/frameworks-index.txt:
That's it. The registry loads it automatically based on the language: key.
4. Verify¶
Re-scan the project. The false positives you identified in step 1 should be gone. If new ones appeared, add those too.
Run the build to make sure nothing broke:
Classification guidelines¶
Some common patterns and how to classify them:
| Pattern | Category | Reasoning |
|---|---|---|
| Getters and property accessors | cheap | Returning a cached value |
Builder-pattern calls (.header(), .uri()) |
cheap | Configuring state, no I/O |
| Repository/DAO query methods | cheap | Real work, but expected in loops |
Assertions (notNull, isTrue) |
trivial | Pure validation, effectively free |
| String/collection utility methods | trivial | Simple transformations, no allocation |
Type-check methods (isProxy, isArray) |
trivial | Boolean check on existing state |
| Connection/context factories | heavyweight | Opens resources, allocates pools |
| Template classes (JdbcTemplate, etc.) | heavyweight | Wraps connection management |
Edge cases¶
Framework method that wraps I/O (e.g. restTemplate.getForObject()): classify as cheap, not heavyweight. The method call itself isn't expensive — the issue is calling it N times in a loop, which is caught by n-plus-one-query or redundant-expensive-call. Heavyweight is for construction, not invocation.
Method you're unsure about: leave it out. It's better to have a false positive that someone reports than a false negative that hides a real issue. You can always add methods later.
Method that exists in multiple frameworks (e.g. of(), get()): this is fine. Method names in the YAML are not qualified — they match by name only. If of() is cheap in your framework, adding it won't break other frameworks that also have an of() method, because overlays are scoped by language.