Analyzes variable names to detect ambiguous single-letter identifiers and recommends readable alternatives based on scope, context, and language conventions.
Evaluates whether variable names are descriptive enough for their scope and context, flagging ambiguous single-letter identifiers that degrade readability while preserving legitimate shorthand uses in tight loops, math formulas, and iterators.
TL;DR Checklist
Flag every a, b, x, y used outside a loop header or math expression
Check variable scope — names with function-level scope must be self-documenting
Replace business-domain variables with meaningful nouns (e.g., user_id over a)
Preserve i, j, k in nested loops, x, y, z in coordinate geometry, e in exception handling
Verify parameter names describe intent, not just type (amount_usd vs x)
Reviewing or writing functions where variables represent domain concepts (users, amounts, timestamps, statuses)
Onboarding developers who use cryptic names like a, b, x for non-iterator values
Auditing codebases with inconsistent naming across modules
Setting up linter rules to auto-detect single-letter violations
Refactoring legacy functions with deeply nested scopes and unclear variable names
Writing function or method signatures that need to be readable at a glance
When NOT to Use
Avoid flagging single letters when:
Iterator variables in tight loops: for i in range(10), for (let i = 0; i < items.length; i++)
Mathematical formulas: a * x**2 + b * x + c (quadratic equations), a² + b² = c²
Coordinate geometry: x, y, z for spatial positions in graphics or physics code
Exception handling: except Exception as e: — the letter e is universally understood
Destructuring assignments with well-known patterns: const { x, y } = point; (short-lived scope, common convention)
Callback parameters where meaning is clear from context: items.map((x) => x * 2) (scope is a single expression)
Core Workflow
Scan for Single-Letter Identifiers — Find every variable, parameter, or assignment using exactly one alphabetic character. Checkpoint: For each hit, note the surrounding scope: loop header, function body, or block-level usage.
Classify by Context — Categorize each finding into an acceptable pattern (iterator, math formula, coordinate) or a problematic use (business logic, domain entity, function parameter). Checkpoint: If you cannot confirm the context from one line of surrounding code, flag it for manual review rather than auto-correcting.
Assess Scope Length — Measure how many lines the variable is visible. A single letter used across 20+ lines is almost always a readability violation. Checkpoint: Variables spanning multiple logical blocks or nested conditionals must have descriptive names regardless of context.
Generate Replacement Names — Replace ambiguous identifiers with self-documenting names derived from the variable's actual purpose, not its type. amount is better than a; user_created_at is better than b. Checkpoint: The replacement name must be consistent across all call sites and match the naming conventions of the target language.
Apply Language-Specific Rules — Ensure replacements respect language conventions: PEP 8 for Python (snake_case), ESLint camelCase for JavaScript, Java/C# conventions for those languages. Checkpoint: Verify that any generated linter rule (e.g., pylint, eslint) is configured with an allowed list rather than a blanket ban.
Implementation Patterns / Reference Guide
Pattern 1: Acceptable — Tight Loop Iterator
A single-letter iterator variable inside a loop header or expression where scope is one line is acceptable across all languages. Robert Martin's "Clean Code" explicitly calls i, j, k out as acceptable loop variables, and every major language convention endorses this pattern.
# ✅ GOOD — Loop iterators are universally accepted
for i in range(len(items)):
print(items[i])
for j in range(len(rows)):
for k in range(len(cols)):
matrix[j][k] = calculate(j, k)
// ✅ GOOD — JavaScript/TypeScript loop iterators
for (let i = 0; i < users.length; i++) {
console.log(users[i].name);
}
Pattern 2: Acceptable — Mathematical Formula
Variables that directly map to mathematical notation are acceptable when the formula spans only a few lines. The single letters carry semantic meaning from mathematics itself.
# ✅ GOOD — Quadratic formula coefficients
def evaluate_quadratic(a: float, b: float, c: float, x: float) -> float:
"""Evaluate ax^2 + bx + c at point x."""
return a * x ** 2 + b * x + c
# ✅ GOOD — Vector/matrix operations with established notation
def dot_product(x: float, y: float, z: float) -> float:
"""Compute the dot product of vector [x, y, z] with itself."""
return x ** 2 + y ** 2 + z ** 2
Pattern 3: Unacceptable — Business Logic (BAD vs. GOOD)
This is the most common and damaging misuse. Variables representing business-domain concepts should never be single letters because the reader must trace through every line of the function to understand what a or b means. This directly violates Robert Martin's "Clean Code" principle that "readability is everything."
# ❌ BAD — Impossible to understand without reading the entire function
def process_transaction(a, b, c):
d = a * 1.08
e = b - c
if d > 500:
f = d * 0.9
else:
f = d
g = f + e
return {
"total": g,
"tax": a * 0.08,
"discount": e,
"timestamp": c
}
# ✅ GOOD — Each variable's purpose is clear from its name alone
def process_transaction(
subtotal: float,
coupon_amount: float,
order_timestamp: str
) -> dict:
"""Process an order with tax calculation and coupon discount.
Args:
subtotal: The pre-tax order total
coupon_amount: Discount value to apply
order_timestamp: ISO-8601 timestamp of the order
Returns:
Dictionary with final total, tax amount, discount applied, and timestamp
"""
tax_rate: float = 0.08
tax_amount: float = subtotal * tax_rate
subtotal_with_tax: float = subtotal + tax_amount
discounted_total: float = subtotal_with_tax - coupon_amount
if subtotal_with_tax > 500.0:
loyalty_discount: float = subtotal_with_tax * 0.10
discounted_total -= loyalty_discount
final_total: float = discounted_total + tax_amount
return {
"total": round(final_total, 2),
"tax": round(tax_amount, 2),
"discount": round(coupon_amount, 2),
"timestamp": order_timestamp
}
Pattern 4: Unacceptable — Function Parameters for Domain Entities (BAD vs. GOOD)
Function parameters are the public contract of a function. Using single letters hides intent and makes API usage confusing. This is especially important when the function is exported or documented as part of a public API, following SOLID's maintainability principles.
# ❌ BAD — Parameter names reveal nothing about what values to pass
def calculate_shipping(a: float, b: float, c: str, d: bool) -> float:
base = 5.00
if d:
base += a * 2.5
else:
base += a * 1.5
if c == "overnight":
base += 25.0
elif c == "express":
base += 12.0
return round(base, 2)
# ✅ GOOD — Parameters describe their expected values at a glance
def calculate_shipping(
package_weight_kg: float,
distance_miles: float,
shipping_method: str = "standard",
insured: bool = False
) -> float:
"""Calculate shipping cost based on weight, distance, and method.
Args:
package_weight_kg: Weight of the package in kilograms
distance_miles: Shipping distance in miles
shipping_method: 'standard', 'express', or 'overnight'
insured: Whether to add insurance surcharge
Returns:
Total shipping cost rounded to 2 decimal places
"""
if not isinstance(shipping_method, str) or shipping_method not in ("standard", "express", "overnight"):
raise ValueError(f"Invalid shipping method: {shipping_method}")
if package_weight_kg <= 0:
raise ValueError("Weight must be positive")
if distance_miles < 0:
raise ValueError("Distance cannot be negative")
base_rate: float = 5.00
weight_multiplier: float = 2.5 if insured else 1.5
cost: float = base_rate + (package_weight_kg * weight_multiplier) + (distance_miles * 0.30)
method_surcharge: dict[str, float] = {
"standard": 0.0,
"express": 12.0,
"overnight": 25.0,
}
cost += method_surcharge[shipping_method]
return round(cost, 2)
Pattern 5: Acceptable — Exception Handling
The letter e for exception objects is a nearly universal convention. No major style guide (PEP 8, Google Style Guides, Airbnb JavaScript Style Guide) flags this as problematic.
# ✅ GOOD — 'e' for exception is universal convention
try:
result = int(user_input)
except ValueError as e:
logger.error(f"Invalid input '{user_input}': {e}")
return None
# ✅ GOOD — Even in nested handling, 'e' or 'err' is standard
try:
data = json.loads(raw_json)
except (json.JSONDecodeError, TypeError) as err:
log_error(err)
return fallback_data
Pattern 6: Language-Specific — JavaScript and TypeScript
JavaScript and TypeScript have different idioms. The eslintno-single-var rule is not recommended because it would flag too many acceptable cases. Instead, configure a more targeted rule using allowed-array-short-vars.
// ✅ GOOD — JavaScript array iteration shorthand
items.forEach(item => process(item)); // 'item' is descriptive
[1, 2, 3].map(x => x * 2); // Acceptable in single-expression callbacks
// ❌ BAD — Business logic variables in JS
function filterUsers(a, b) {
const c = a.filter(user => user.age > b);
return c.map(u => ({ name: u.name, id: u.id }));
}
// ✅ GOOD — Descriptive parameter names
function filterActiveUsersByAge(
users: User[],
minimumAge: number
): Omit<User, 'password'>[] {
const eligibleUsers = users.filter(user => user.age > minimumAge);
return eligibleUsers.map(({ name, id }) => ({ name, id }));
}
Pattern 7: Language-Specific — Java and C#
Java and C# developers often face additional pressure from auto-generated code (ORM frameworks, serializers) that may use single-letter fields. The skill guides refactoring these while maintaining compatibility.
// ✅ GOOD — Auto-generated field names are an exception; business logic must be descriptive
@Entity
public class Order {
private Long id; // OK: framework-generated identifier
private String customerId; // Good: self-documenting
}
// ❌ BAD — Hand-written business logic with single-letter parameters
public BigDecimal processPayment(BigDecimal a, BigDecimal b) {
return a.multiply(b); // What are these? Amount? Rate? Tax?
}
// ✅ GOOD — Descriptive parameter names in Java
public BigDecimal calculateTotalWithTax(
BigDecimal subtotal,
BigDecimal taxRate
) {
Objects.requireNonNull(subtotal, "Subtotal must not be null");
if (taxRate.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Tax rate cannot be negative");
}
return subtotal.multiply(BigDecimal.ONE.add(taxRate));
}
Constraints
MUST DO
Require self-documenting names for all variables with function-level scope or wider — if a reader needs to look at the variable's value assignments to understand it, the name is insufficient
Preserve legitimate shorthand in loop headers (i, j, k), math expressions (x, y, z in coordinate contexts), and exception handlers (e)
Derive replacement names from purpose, not type — amount_usd describes what the value is; val1 describes nothing about intent
Configure linters with allowed lists, not blanket bans — a global no-single-var rule creates noise by flagging acceptable iterator usage
Use typed signatures on all functions reviewed or written under this skill to make parameter intent explicit at call sites
Check naming consistency across the entire scope — if you rename a to user_id, ensure it is used consistently and not mixed with any remaining single-letter variables in the same function
MUST NOT DO
Flag iterator variables in loop headers (for i in range(...)), as this violates universal conventions documented in PEP 8, Google Style Guides, and every major style reference
Replace mathematical notation variables (x, y in geometry; a, b, c in polynomial expressions) — the semantics come from the mathematical context, not the code
Use single-letter names for parameters of exported/public functions — these are part of the API contract and must be readable without documentation
Create overlong descriptive names that exceed 40 characters per variable (customer_information_timestamp_recorded is worse than created_at) — follow Clean Code's principle of concise but clear naming
Ignore language-specific conventions — Python uses snake_case, JavaScript/TypeScript uses camelCase, Java/C# uses PascalCase for classes and camelCase for members; the naming style must match
Output Template
When this skill is active, the model's output must contain:
Violation Summary — A numbered list of each single-letter identifier violation found, with file path, line number, variable name, and estimated scope (number of lines visible)
Replacement Recommendation — For each violation, provide a concrete replacement name with rationale explaining why the new name is more descriptive than the original
Refactored Code Block — A complete before/after code snippet showing the corrected function or method with all replacements applied consistently
Lint Configuration Suggestion — Recommended linter rule configuration (e.g., .eslintrc, pyproject.toml) to prevent future violations, including any allowed-list entries for acceptable shorthand
Acceptable Exceptions — Any single-letter variables in the scanned code that are correctly used and should not be changed, with justification
Related Skills
Skill
Purpose
code-review
Broader code quality methodology that includes naming conventions as one aspect of maintainability
refactoring-legacy-code
Techniques for incrementally renaming variables in legacy codebases without breaking tests or API contracts
dry-principles
Complementary principle — descriptive naming supports DRY by making extracted functions self-documenting and easier to reuse
Language-Specific Quick Reference
Language
Loop Iterators
Exception Variables
Business Logic
Allowed Config
Python (PEP 8)
i, j, k
e, err
Never single letter
pylint: disable W0622 for iter, input; enable naming rules via pycodestyle
JavaScript/TS (ESLint)
i, j, k
err, error
Never single letter
No native rule; use eslint-plugin-no-single-var with exceptions
Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.
This skill operationalizes Robert Martin's "Clean Code" chapter on variables: "The name of a variable, function, or class should answer all the big questions. It should tell you why it exists, what it does, and how it is used." Referenced standards include Clean Code (Martin, 2008), PEP 8 (Python Enhancement Proposal 8), SOLID principles (maintainability aspect), and DRY principle.
1---2name: single-letter-variables3description: Analyzes variable names to detect ambiguous single-letter identifiers and recommends readable alternatives based on scope, context, and language conventions.4license: MIT5---678910# Single-Letter Variable Naming Conventions1112Evaluates whether variable names are descriptive enough for their scope and context, flagging ambiguous single-letter identifiers that degrade readability while preserving legitimate shorthand uses in tight loops, math formulas, and iterators.1314## TL;DR Checklist1516- [ ] Flag every `a`, `b`, `x`, `y` used outside a loop header or math expression17- [ ] Check variable scope — names with function-level scope must be self-documenting18- [ ] Replace business-domain variables with meaningful nouns (e.g., `user_id` over `a`)19- [ ] Preserve `i`, `j`, `k` in nested loops, `x`, `y`, `z` in coordinate geometry, `e` in exception handling20- [ ] Verify parameter names describe intent, not just type (`amount_usd` vs `x`)21- [ ] Ensure multi-language codebases follow language-specific conventions (PEP 8, ESLint rules)2223---2425## When to Use2627Use this skill when:2829- Reviewing or writing functions where variables represent domain concepts (users, amounts, timestamps, statuses)30- Onboarding developers who use cryptic names like `a`, `b`, `x` for non-iterator values31- Auditing codebases with inconsistent naming across modules32- Setting up linter rules to auto-detect single-letter violations33- Refactoring legacy functions with deeply nested scopes and unclear variable names34- Writing function or method signatures that need to be readable at a glance3536---3738## When NOT to Use3940Avoid flagging single letters when:4142- **Iterator variables in tight loops**: `for i in range(10)`, `for (let i = 0; i < items.length; i++)`43- **Mathematical formulas**: `a * x**2 + b * x + c` (quadratic equations), `a² + b² = c²`44- **Coordinate geometry**: `x`, `y`, `z` for spatial positions in graphics or physics code45- **Exception handling**: `except Exception as e:` — the letter `e` is universally understood46- **Destructuring assignments with well-known patterns**: `const { x, y } = point;` (short-lived scope, common convention)47- **Callback parameters where meaning is clear from context**: `items.map((x) => x * 2)` (scope is a single expression)4849---5051## Core Workflow52531. **Scan for Single-Letter Identifiers** — Find every variable, parameter, or assignment using exactly one alphabetic character. **Checkpoint:** For each hit, note the surrounding scope: loop header, function body, or block-level usage.54552. **Classify by Context** — Categorize each finding into an acceptable pattern (iterator, math formula, coordinate) or a problematic use (business logic, domain entity, function parameter). **Checkpoint:** If you cannot confirm the context from one line of surrounding code, flag it for manual review rather than auto-correcting.56573. **Assess Scope Length** — Measure how many lines the variable is visible. A single letter used across 20+ lines is almost always a readability violation. **Checkpoint:** Variables spanning multiple logical blocks or nested conditionals must have descriptive names regardless of context.58594. **Generate Replacement Names** — Replace ambiguous identifiers with self-documenting names derived from the variable's actual purpose, not its type. `amount` is better than `a`; `user_created_at` is better than `b`. **Checkpoint:** The replacement name must be consistent across all call sites and match the naming conventions of the target language.60615. **Apply Language-Specific Rules** — Ensure replacements respect language conventions: PEP 8 for Python (`snake_case`), ESLint `camelCase` for JavaScript, Java/C# conventions for those languages. **Checkpoint:** Verify that any generated linter rule (e.g., `pylint`, `eslint`) is configured with an allowed list rather than a blanket ban.6263---6465## Implementation Patterns / Reference Guide6667### Pattern 1: Acceptable — Tight Loop Iterator6869A single-letter iterator variable inside a loop header or expression where scope is one line is acceptable across all languages. Robert Martin's "Clean Code" explicitly calls `i`, `j`, `k` out as acceptable loop variables, and every major language convention endorses this pattern.7071```python72# ✅ GOOD — Loop iterators are universally accepted73for i in range(len(items)):74 print(items[i])7576for j in range(len(rows)):77 for k in range(len(cols)):78 matrix[j][k] = calculate(j, k)79```8081```typescript82// ✅ GOOD — JavaScript/TypeScript loop iterators83for (let i = 0; i < users.length; i++) {84 console.log(users[i].name);85}86```8788### Pattern 2: Acceptable — Mathematical Formula8990Variables that directly map to mathematical notation are acceptable when the formula spans only a few lines. The single letters carry semantic meaning from mathematics itself.9192```python93# ✅ GOOD — Quadratic formula coefficients94def evaluate_quadratic(a: float, b: float, c: float, x: float) -> float:95 """Evaluate ax^2 + bx + c at point x."""96 return a * x ** 2 + b * x + c9798# ✅ GOOD — Vector/matrix operations with established notation99def dot_product(x: float, y: float, z: float) -> float:100 """Compute the dot product of vector [x, y, z] with itself."""101 return x ** 2 + y ** 2 + z ** 2102```103104### Pattern 3: Unacceptable — Business Logic (BAD vs. GOOD)105106This is the most common and damaging misuse. Variables representing business-domain concepts should never be single letters because the reader must trace through every line of the function to understand what `a` or `b` means. This directly violates Robert Martin's "Clean Code" principle that "readability is everything."107108```python109# ❌ BAD — Impossible to understand without reading the entire function110def process_transaction(a, b, c):111 d = a * 1.08112 e = b - c113 if d > 500:114 f = d * 0.9115 else:116 f = d117 g = f + e118 return {119 "total": g,120 "tax": a * 0.08,121 "discount": e,122 "timestamp": c123 }124125# ✅ GOOD — Each variable's purpose is clear from its name alone126def process_transaction(127 subtotal: float,128 coupon_amount: float,129 order_timestamp: str130) -> dict:131 """Process an order with tax calculation and coupon discount.132133 Args:134 subtotal: The pre-tax order total135 coupon_amount: Discount value to apply136 order_timestamp: ISO-8601 timestamp of the order137138 Returns:139 Dictionary with final total, tax amount, discount applied, and timestamp140 """141 tax_rate: float = 0.08142 tax_amount: float = subtotal * tax_rate143 subtotal_with_tax: float = subtotal + tax_amount144 discounted_total: float = subtotal_with_tax - coupon_amount145146 if subtotal_with_tax > 500.0:147 loyalty_discount: float = subtotal_with_tax * 0.10148 discounted_total -= loyalty_discount149150 final_total: float = discounted_total + tax_amount151152 return {153 "total": round(final_total, 2),154 "tax": round(tax_amount, 2),155 "discount": round(coupon_amount, 2),156 "timestamp": order_timestamp157 }158```159160### Pattern 4: Unacceptable — Function Parameters for Domain Entities (BAD vs. GOOD)161162Function parameters are the public contract of a function. Using single letters hides intent and makes API usage confusing. This is especially important when the function is exported or documented as part of a public API, following SOLID's maintainability principles.163164```python165# ❌ BAD — Parameter names reveal nothing about what values to pass166def calculate_shipping(a: float, b: float, c: str, d: bool) -> float:167 base = 5.00168 if d:169 base += a * 2.5170 else:171 base += a * 1.5172 if c == "overnight":173 base += 25.0174 elif c == "express":175 base += 12.0176 return round(base, 2)177178# ✅ GOOD — Parameters describe their expected values at a glance179def calculate_shipping(180 package_weight_kg: float,181 distance_miles: float,182 shipping_method: str = "standard",183 insured: bool = False184) -> float:185 """Calculate shipping cost based on weight, distance, and method.186187 Args:188 package_weight_kg: Weight of the package in kilograms189 distance_miles: Shipping distance in miles190 shipping_method: 'standard', 'express', or 'overnight'191 insured: Whether to add insurance surcharge192193 Returns:194 Total shipping cost rounded to 2 decimal places195 """196 if not isinstance(shipping_method, str) or shipping_method not in ("standard", "express", "overnight"):197 raise ValueError(f"Invalid shipping method: {shipping_method}")198 if package_weight_kg <= 0:199 raise ValueError("Weight must be positive")200 if distance_miles < 0:201 raise ValueError("Distance cannot be negative")202203 base_rate: float = 5.00204 weight_multiplier: float = 2.5 if insured else 1.5205 cost: float = base_rate + (package_weight_kg * weight_multiplier) + (distance_miles * 0.30)206207 method_surcharge: dict[str, float] = {208 "standard": 0.0,209 "express": 12.0,210 "overnight": 25.0,211 }212 cost += method_surcharge[shipping_method]213214 return round(cost, 2)215```216217### Pattern 5: Acceptable — Exception Handling218219The letter `e` for exception objects is a nearly universal convention. No major style guide (PEP 8, Google Style Guides, Airbnb JavaScript Style Guide) flags this as problematic.220221```python222# ✅ GOOD — 'e' for exception is universal convention223try:224 result = int(user_input)225except ValueError as e:226 logger.error(f"Invalid input '{user_input}': {e}")227 return None228229# ✅ GOOD — Even in nested handling, 'e' or 'err' is standard230try:231 data = json.loads(raw_json)232except (json.JSONDecodeError, TypeError) as err:233 log_error(err)234 return fallback_data235```236237### Pattern 6: Language-Specific — JavaScript and TypeScript238239JavaScript and TypeScript have different idioms. The `eslint` `no-single-var` rule is not recommended because it would flag too many acceptable cases. Instead, configure a more targeted rule using `allowed-array-short-vars`.240241```javascript242// ✅ GOOD — JavaScript array iteration shorthand243items.forEach(item => process(item)); // 'item' is descriptive244[1, 2, 3].map(x => x * 2); // Acceptable in single-expression callbacks245246// ❌ BAD — Business logic variables in JS247function filterUsers(a, b) {248 const c = a.filter(user => user.age > b);249 return c.map(u => ({ name: u.name, id: u.id }));250}251252// ✅ GOOD — Descriptive parameter names253function filterActiveUsersByAge(254 users: User[],255 minimumAge: number256): Omit<User, 'password'>[] {257 const eligibleUsers = users.filter(user => user.age > minimumAge);258 return eligibleUsers.map(({ name, id }) => ({ name, id }));259}260```261262### Pattern 7: Language-Specific — Java and C#263264Java and C# developers often face additional pressure from auto-generated code (ORM frameworks, serializers) that may use single-letter fields. The skill guides refactoring these while maintaining compatibility.265266```java267// ✅ GOOD — Auto-generated field names are an exception; business logic must be descriptive268@Entity269public class Order {270 private Long id; // OK: framework-generated identifier271 private String customerId; // Good: self-documenting272}273274// ❌ BAD — Hand-written business logic with single-letter parameters275public BigDecimal processPayment(BigDecimal a, BigDecimal b) {276 return a.multiply(b); // What are these? Amount? Rate? Tax?277}278279// ✅ GOOD — Descriptive parameter names in Java280public BigDecimal calculateTotalWithTax(281 BigDecimal subtotal,282 BigDecimal taxRate283) {284 Objects.requireNonNull(subtotal, "Subtotal must not be null");285 if (taxRate.compareTo(BigDecimal.ZERO) < 0) {286 throw new IllegalArgumentException("Tax rate cannot be negative");287 }288 return subtotal.multiply(BigDecimal.ONE.add(taxRate));289}290```291292---293294## Constraints295296### MUST DO297298- **Require self-documenting names** for all variables with function-level scope or wider — if a reader needs to look at the variable's value assignments to understand it, the name is insufficient299- **Preserve legitimate shorthand** in loop headers (`i`, `j`, `k`), math expressions (`x`, `y`, `z` in coordinate contexts), and exception handlers (`e`)300- **Derive replacement names from purpose, not type** — `amount_usd` describes *what* the value is; `val1` describes nothing about intent301- **Configure linters with allowed lists**, not blanket bans — a global `no-single-var` rule creates noise by flagging acceptable iterator usage302- **Use typed signatures** on all functions reviewed or written under this skill to make parameter intent explicit at call sites303- **Check naming consistency across the entire scope** — if you rename `a` to `user_id`, ensure it is used consistently and not mixed with any remaining single-letter variables in the same function304305### MUST NOT DO306307- **Flag iterator variables in loop headers** (`for i in range(...)`), as this violates universal conventions documented in PEP 8, Google Style Guides, and every major style reference308- **Replace mathematical notation variables** (`x`, `y` in geometry; `a`, `b`, `c` in polynomial expressions) — the semantics come from the mathematical context, not the code309- **Use single-letter names for parameters of exported/public functions** — these are part of the API contract and must be readable without documentation310- **Create overlong descriptive names** that exceed 40 characters per variable (`customer_information_timestamp_recorded` is worse than `created_at`) — follow Clean Code's principle of concise but clear naming311- **Ignore language-specific conventions** — Python uses `snake_case`, JavaScript/TypeScript uses `camelCase`, Java/C# uses `PascalCase` for classes and `camelCase` for members; the naming style must match312313---314315## Output Template316317When this skill is active, the model's output must contain:3183191. **Violation Summary** — A numbered list of each single-letter identifier violation found, with file path, line number, variable name, and estimated scope (number of lines visible)3202. **Replacement Recommendation** — For each violation, provide a concrete replacement name with rationale explaining why the new name is more descriptive than the original3213. **Refactored Code Block** — A complete before/after code snippet showing the corrected function or method with all replacements applied consistently3224. **Lint Configuration Suggestion** — Recommended linter rule configuration (e.g., `.eslintrc`, `pyproject.toml`) to prevent future violations, including any `allowed-list` entries for acceptable shorthand3235. **Acceptable Exceptions** — Any single-letter variables in the scanned code that are correctly used and should not be changed, with justification324325---326327## Related Skills328329| Skill | Purpose |330|---|---|331| `code-review` | Broader code quality methodology that includes naming conventions as one aspect of maintainability |332| `refactoring-legacy-code` | Techniques for incrementally renaming variables in legacy codebases without breaking tests or API contracts |333| `dry-principles` | Complementary principle — descriptive naming supports DRY by making extracted functions self-documenting and easier to reuse |334335---336337## Language-Specific Quick Reference338339| Language | Loop Iterators | Exception Variables | Business Logic | Allowed Config |340|----------|---------------|--------------------|----------------|----------------|341| Python (PEP 8) | `i, j, k` | `e, err` | **Never** single letter | `pylint`: disable `W0622` for `iter`, `input`; enable naming rules via `pycodestyle` |342| JavaScript/TS (ESLint) | `i, j, k` | `err, error` | **Never** single letter | No native rule; use `eslint-plugin-no-single-var` with exceptions |343| Java (Sun/Oracle Style Guide) | `i, j, k` | `e` | **Never** single letter | `checkstyle`: `LocalVariableName` check — regex `^[a-z]([a-z0-9][a-zA-Z0-9]*)?$` |344| C# (Microsoft Guidelines) | `i, j, k` | `ex, e` | **Never** single letter | `StyleCop`: SA1311 — descriptive names required |345346---347348## Live References349350> Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.351352- [Google Python Style Guide — Naming Conventions (s3.1)](https://google.github.io/styleguide/pyguide.html#s3.1-identifiers)353- [PEP 8 — Style Guide for Python Code: Naming Conventions](https://peps.python.org/pep-0008/#naming-conventions)354- [Microsoft C# Naming Guidelines — .NET Documentation](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/naming-conventions)355- [Mozilla MDN — JavaScript Naming Conventions Best Practices](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Code_style/naming_guidelines)356- [Sonatype — Code Style Guide for Java Developers](https://www.sonatype.com/hubfs/Code%20Style%20Guide.pdf)357358---359360*This skill operationalizes Robert Martin's "Clean Code" chapter on variables: "The name of a variable, function, or class should answer all the big questions. It should tell you why it exists, what it does, and how it is used." Referenced standards include Clean Code (Martin, 2008), PEP 8 (Python Enhancement Proposal 8), SOLID principles (maintainability aspect), and DRY principle.*
Run npx skillmds@latest add paulpas/single-letter-variables in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Analyzes variable names to detect ambiguous single-letter identifiers and recommends readable alternatives based on scope, context, and language conventions. It is listed under Coding & Dev Tools on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free. This skill is licensed under MIT.
paulpas (@paulpas) published this skill. Their other Agent Skills are listed on their SkillMD profile.