# iRules performance linter

> Paste an iRule; it flags F5-documented performance anti-patterns line by line, with severity, cause, and fix. The big one: global variables demote CMP. Local, no network.

- Tool: https://ronutz.com/en/tools/f5-irules-performance-linter
- Family: Networking

---

## What it does

Paste an iRule; the linter flags a small set of high-confidence, F5-documented performance and correctness anti-patterns, line by line, each with a severity, the offending token, why it matters, and the fix. It is a static scanner (no Tcl parser, no execution), so it deliberately covers only patterns a line scan can catch reliably and with few false positives. It is not a substitute for measurement: pair it with the iRules Runtime Calculator, which reads real timing statistics, for the actual cost of a rule. Everything runs locally.

## What it flags

**Global-namespace variables** (`$::x`, `set ::x`, the `global` keyword) are flagged HIGH. F5's validator catches the `global` keyword as of v10 and demotes the virtual server to a single TMM instance (CMP demotion), so one processor handles all of that virtual's connections; global variables have been deprecated since v10. The obsolete `$::datagroup` form is worse still: on version 11 and later, accessing a data group that way raises a TCL runtime error and sends a reset to the client. The fix is the CMP-safe `static::` namespace for shared constants, ordinary local variables for per-connection data, or the `class` command for data groups.

**`expr` without braces** is a WARNING. Tcl substitutes and re-parses the unbraced form, and can double-substitute; bracing the expression lets the bytecode compiler optimize it. The fix is `expr { ... }`.

**`matchclass` / `findclass`** are INFO. Both were deprecated in v10 in favor of the `class` command, which F5 describes as offering better functionality and performance. The fix is `class match` / `class search`.

**`regexp` / `regsub`** are INFO. Regular expressions cost materially more than fixed string work; when the match is a prefix, suffix, exact value, or simple glob, `string`, `scan`, `switch -glob`, or `class` are cheaper.

## What it deliberately does not flag

`static::` variables (CMP-safe), `class match` / `class search`, and braced `expr` are correct and pass clean. Persistence, tables, and the `session` command are CMP-compatible on modern versions, so they are not treated as CMP problems. Full-line comments are skipped.

## Standards and references

- [F5 clouddocs — iRules CMP Compatibility](https://clouddocs.f5.com/api/irules/CMPCompatibility.html) - the global keyword is caught by the validator as of v10 and demotes the virtual server; static:: variables were added for CMP-safe shared values; persistence and tables are CMP-compatible on modern versions
- [F5 clouddocs — RULE_INIT](https://clouddocs.f5.com/api/irules/RULE_INIT.html) - global variables modified by an iRule demote the virtual to a single processor core; see CMP Compatibility
- [F5 clouddocs — class (and matchclass)](https://clouddocs.f5.com/api/irules/class.html) - the class command deprecates findclass and matchclass (better functionality and performance); the obsolete $::datagroup access demotes CMP on 9.4.4-10 and raises a TCL runtime error, resetting the client, on v11+
- [F5 clouddocs — How To Write Fast Rules](https://clouddocs.f5.com/api/irules/HowToWriteFastRules.html) - brace expr expressions so the bytecode compiler can optimize them, rather than paying for string re-substitution; general iRule performance guidance

## Related reading

- [iRules procedures: reusable code with proc and call](https://ronutz.com/en/learn/irules-procedures-proc-and-call.md): Since BIG-IP 11.4, an iRule can define a block of code once as a procedure and call it from anywhere, including from other iRules that are not attached to any virtual server. Here is how proc and call work, how arguments and defaults are declared, and the one namespace rule that trips everyone up.
- [iRules, CMP, and the static:: namespace](https://ronutz.com/en/learn/irules-cmp-and-static-namespace.md): Clustered Multiprocessing spreads a virtual server's traffic across every TMM, but one construct quietly undoes it: a global variable demotes the virtual to a single core. This is why globals are deprecated, what the static:: namespace and data groups do instead, and how RULE_INIT ties them together.
- [iRules: choosing between if, switch, class match, and static arrays](https://ronutz.com/en/learn/irules-branching-and-lookups.md): When an iRule has to match one value against many and act, there are four common tools: chained if, switch, a data group with class match, and a directly-indexed static:: array. F5 has actually ranked them for speed. Here is that ranking, and the two questions that decide which one you should reach for.
- [Loops and lists in iRules, and why they block](https://ronutz.com/en/learn/irules-loops-and-lists.md): iRules support foreach, for, and while, and most of Tcl's list commands. But loops run inline in a single-threaded TMM, so a large loop per request stalls that connection. Here is which loop to use, which list commands you actually have, and why foreach is the one to reach for first.
