ワンクリックで
localizing-variables
Declare variables in smallest possible scope, initialize close to first use, minimize span and live time
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Declare variables in smallest possible scope, initialize close to first use, minimize span and live time
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Fork, clone to ~/.clank, run installer, edit CLAUDE.md
RED-GREEN-REFACTOR for process documentation - baseline without skill, write addressing failures, iterate closing loopholes
Skills wiki intro - mandatory workflows, search tool, brainstorming triggers
Interactive idea refinement using Socratic method to develop fully-formed designs
Execute detailed plans in batches with review checkpoints
Execute implementation plan by dispatching fresh subagent for each task, with code review between tasks
| name | Localizing Variables |
| description | Declare variables in smallest possible scope, initialize close to first use, minimize span and live time |
| when_to_use | When writing any code with variables. When variables are declared at top of function but used later. When related statements are scattered. When variable scope is larger than necessary. When you see long variable live times or large span between references. When can't find where variable is initialized. When variable has stale or unexpected value. When forgot to reset counter or accumulator. When initialization errors occur. When variables declared far from first use. When window of vulnerability is large. When must scroll to see variable declaration and usage. When reviewing pull requests with wide variable scope. When refactoring functions with many local variables. |
| version | 1.0.0 |
| languages | all |
The Principle of Proximity: Keep related actions together. Declare variables in the smallest scope possible, initialize them close to where they're first used, and keep all references to a variable close together.
Core principle: Minimize the window of vulnerability. The smaller the scope and the closer the references, the less can go wrong and the easier code is to understand.
Goal: Reduce what you must keep in mind at any one time.
Apply to every variable you declare:
Warning signs:
How widely visible a variable is:
{} or indented block (smallest)Rule: Start with smallest scope. Expand only if necessary.
Distance between successive references to a variable:
a = 0 # First reference
b = 0 # 1 line between references to a
c = 0 # 2 lines between references to a
a = b + c # Second reference
# Span of a: 2 lines
Goal: Minimize span. Keep references close together.
Total statements between first and last reference:
recordIndex = 0 # Line 2 - first reference
# ... 24 lines of other code ...
recordIndex += 1 # Line 28 - last reference
# Live time: 28 - 2 + 1 = 27 statements
Goal: Minimize live time. Reduce window of vulnerability.
Keep related actions together:
❌ Bad (declarations far from use):
def process_data():
# All declarations at top
index = 0
total = 0
done = False
result = []
# 20 lines later...
while index < count:
index += 1
# 30 lines later...
while not done:
if total > threshold:
done = True
# 40 lines later...
result.append(final_value)
return result
Live times: index=25, total=35, done=35, result=40. Average: 34 lines.
✅ Good (declare close to use):
def process_data():
# Declare index right before loop that uses it
index = 0
while index < count:
index += 1
# Declare total and done right before loop that uses them
total = 0
done = False
while not done:
if total > threshold:
done = True
# Declare result right before use
result = []
result.append(final_value)
return result
Live times: index=3, total=5, done=5, result=2. Average: 4 lines.
Improvement: 34 → 4 average live time (8.5x better)
Languages like C++, Java, Python, JavaScript allow this:
❌ Bad:
def calculate_report():
total = 0 # Declared at top
count = 0
average = 0.0
# 10 lines later...
total = sum(values)
count = len(values)
average = total / count if count > 0 else 0.0
✅ Good:
def calculate_report():
# 10 lines of other work...
# Declare right before use
total = sum(values)
count = len(values)
average = total / count if count > 0 else 0.0
In languages supporting block scope, use it:
# Process old data - variables scoped to this block
{
old_data = get_old_data()
old_total = sum(old_data)
print_summary(old_data, old_total)
} # old_data, old_total die here
# Process new data - fresh variables, no collision
{
new_data = get_new_data()
new_total = sum(new_data)
print_summary(new_data, new_total)
} # new_data, new_total die here
❌ Bad:
# Declare and initialize separately
user_count: int
user_count = 0
active_users: list
active_users = []
✅ Good:
# Initialize when declaring
user_count = 0
active_users = []
Many languages support declaring loop variables in the loop:
# Variable i exists ONLY within this loop
for i in range(count):
process(i)
# i doesn't exist here
# Another loop can use i without conflict
for i in range(other_count):
process_other(i)
Keep statements working with same variables together:
❌ Bad (scattered):
old_data = get_old_data()
new_data = get_new_data()
old_total = sum(old_data)
new_total = sum(new_data)
print_old_summary(old_data, old_total)
print_new_summary(new_data, new_total)
Must track 6 variables simultaneously
✅ Good (grouped):
# Group 1: Old data (track 2 variables)
old_data = get_old_data()
old_total = sum(old_data)
print_old_summary(old_data, old_total)
# Group 2: New data (track 2 variables)
new_data = get_new_data()
new_total = sum(new_data)
print_new_summary(new_data, new_total)
Track only 2 variables at a time
Globals have enormous scope, span, and live time - avoid them.
❌ Bad:
total = 0 # Global
def add_to_total(value):
global total
total += value # Total lives forever, visible everywhere
def get_total():
global total
return total
✅ Good:
class Counter:
def __init__(self):
self._total = 0 # Private, encapsulated
def add(self, value):
self._total += value # Scoped to class
def get_total(self):
return self._total
Even better (eliminate state if possible):
def calculate_total(values):
return sum(values) # No state, no scope issues
Count lines between successive references:
value = 10 # Reference 1
line_a() # 1 line between
line_b() # 2 lines between
result = value * 2 # Reference 2
# Span: 2 lines
Target: Average span < 5 lines
Count lines from first to last reference (inclusive):
count = 0 # Line 5 - first reference
# ... code ...
count += 1 # Line 42 - last reference
# Live time: 42 - 5 + 1 = 38 lines
Target: Average live time < 10 lines
Global variables: Infinite live time (another reason to avoid)
❌ All variables at top (C-style):
def process():
# Declare everything at top
i = 0
j = 0
total = 0
result = []
temp = None
# Use i 20 lines later
for i in range(10):
...
✅ Declare where used:
def process():
# Use variables close to declaration
for i in range(10): # i scoped to loop
...
total = 0 # Declared right before use
for item in items:
total += item
❌ Wide scope when narrow would work:
def calculate():
result = 0 # Function scope
if condition_a:
result = calculate_a() # Could be block-scoped
print(result)
# result accessible here but not used
if condition_b:
result = calculate_b() # Reusing same variable
print(result)
✅ Narrow scope:
def calculate():
if condition_a:
result = calculate_a() # Block scope
print(result)
# result doesn't exist here
if condition_b:
result = calculate_b() # Fresh variable, no collision
print(result)
❌ Long live time:
index = 0 # Line 2
# ... 50 lines of unrelated code ...
while index < count: # Line 52 - finally used
index += 1
# Live time: 51 lines
✅ Short live time:
# ... 50 lines of other code ...
index = 0 # Line 52 - right before use
while index < count:
index += 1
# Live time: 2 lines
Languages like C++, Java, Python, JavaScript support this:
# ✅ Declare and initialize together
user_count = 0
active_users = get_active_users()
total_revenue = calculate_revenue(orders)
# ✅ Loop variable scoped to loop
for user in users:
process(user) # user exists only here
for item in items:
handle(item) # item exists only here
❌ Bad:
def process_records():
index = 0 # Line 2
# 30 lines of other work...
# Finally use index
while index < record_count: # Line 32
index += 1
✅ Good:
def process_records():
# 30 lines of other work...
# Initialize right before loop
index = 0
while index < record_count:
index += 1
Why: When you modify code and add outer loop, initialization is correctly placed for re-initialization on each pass.
Long routines create large scope. Break into smaller routines:
# ✅ Each routine has small scope
def process_old_data():
old_data = get_old_data() # Scoped to this routine only
old_total = sum(old_data)
return old_total
def process_new_data():
new_data = get_new_data() # Fresh variable, no collision
new_total = sum(new_data)
return new_total
Variables automatically die when routine exits.
Hierarchy (most restricted to least):
Start at #1, move down only if necessary.
Two philosophies:
"Make variables global so they're convenient to access anywhere. Don't fool around with parameter lists."
Problem: Easy to write, hard to read/maintain. Any routine can modify any variable. Must understand entire program to modify one part.
"Keep variables as local as possible. Hide information. Minimize what you must think about at once."
Benefit: Harder to write (must think about scope), easier to read/maintain. Can understand one routine without knowing all others.
Code Complete's recommendation: Favor intellectual manageability. Code is read 10x more than written.
❌ Before (wide scope, long live time):
def summarize_data():
# All variables at top with function scope
old_data = None
num_old = 0
total_old = 0
new_data = None
num_new = 0
total_new = 0
old_data = get_old_data() # Line 8
num_old = len(old_data)
total_old = sum(old_data)
print_summary(old_data, total_old, num_old) # Line 11
save_summary(total_old, num_old)
new_data = get_new_data() # Line 14
num_new = len(new_data)
total_new = sum(new_data)
print_summary(new_data, total_new, num_new) # Line 17
save_summary(total_new, num_new)
Must track 6 variables throughout entire function. Live times: old_data=4, new_data=4, etc.
✅ After (narrow scope, short live time):
def summarize_data():
# Group 1: Old data (variables live only 4 lines)
old_data = get_old_data()
num_old = len(old_data)
total_old = sum(old_data)
print_summary(old_data, total_old, num_old)
save_summary(total_old, num_old)
# old_data, num_old, total_old mentally "die" here
# Group 2: New data (fresh variables, 4 lines)
new_data = get_new_data()
num_new = len(new_data)
total_new = sum(new_data)
print_summary(new_data, total_new, num_new)
save_summary(total_new, num_new)
Track 3 variables at a time. Same live times, but mental load reduced.
✅ Even better (extract to routines):
def summarize_data():
process_old_data() # Variables scoped inside
process_new_data() # Variables scoped inside
def process_old_data():
# Variables live only in this routine (5 lines)
old_data = get_old_data()
num_old = len(old_data)
total_old = sum(old_data)
print_summary(old_data, total_old, num_old)
save_summary(total_old, num_old)
# Variables die when routine exits
Track 3 variables maximum. Variables automatically cleaned up.
| Situation | Technique | Example |
|---|---|---|
| Loop variable | Declare in loop | for i in range(n): |
| Temporary calculation | Inline or immediate use | total = sum(values) right before print(total) |
| Used in one block | Declare in that block | if-block variable stays in if-block |
| Used across function | Function-local only if necessary | Don't make it class/global |
| Shared across methods | Private instance variable | Not public unless necessary |
| Truly global | Access routine instead | Wrap in getter/setter |
# ✅ Counter scoped to loop
for i in range(len(items)):
process(items[i])
# i doesn't exist here - good
# Can reuse i in another loop without collision
for i in range(len(others)):
process(others[i])
# ✅ Calculate right before use
def generate_report():
# Other work...
# Calculate only when needed, use immediately
total_revenue = sum(order.total for order in orders)
print(f"Total Revenue: ${total_revenue}")
# Different calculation later
active_user_count = len([u for u in users if u.is_active])
print(f"Active Users: {active_user_count}")
# ✅ Temporary lives only 2-3 lines
def swap_values(arr, i, j):
temp = arr[i] # Temporary variable
arr[i] = arr[j]
arr[j] = temp # temp used and done (live time: 3 lines)
# ✅ State variables near the loop they control
def process_until_done():
# Other work...
# Declare state right before loop
done = False
attempts = 0
while not done and attempts < max_attempts:
done = try_process()
attempts += 1
Calculate average span and live time for a function:
def example():
a = 0 # Line 2, ref 1
b = 0 # Line 3
c = 0 # Line 4
a = b + c # Line 5, ref 2 of a
d = a * 2 # Line 6, ref 3 of a
# Span of a: (5-2=3) + (6-5=1) = 4, average 2
# Live time of a: 6-2+1 = 5 lines
Good metrics:
If higher: Consider localizing more aggressively.
Shorter live time = fewer lines where variable could be incorrectly modified:
# Live time = 50 lines
value = 0 # Line 1
# ... 48 lines where value could be accidentally changed ...
return value # Line 50
vs.
# Live time = 2 lines
value = calculate() # Line 49
return value # Line 50 - less can go wrong
Seeing declaration and usage together aids comprehension:
# ✅ See both on one screen
count = len(items)
print(f"Processing {count} items")
# ❌ Must scroll to see declaration
# ... Line 1: count = 0
# ... 50 lines later...
# ... Line 51: print(f"Processing {count} items") # What's count?
Variables initialized close to use are less likely to have stale values:
# ✅ Initialized fresh each loop iteration
for batch in batches:
count = 0 # Reset for each batch
for item in batch:
count += 1
vs.
# ❌ Might forget to reset
count = 0 # Top of function
for batch in batches:
# Forgot to reset count - accumulates across batches!
for item in batch:
count += 1
Short live time makes extracting to separate routine easier:
# Related statements with short-lived variables
# are easy to extract into their own routine
old_data = get_old_data() # Lines 10-13
old_total = sum(old_data)
print_summary(old_data, old_total)
# → Extract to process_old_data() routine
For each variable, ask:
If any answer is "yes, could be smaller" → localize it.
From Code Complete:
Key insight: The more you can hide, the less you must keep in mind. The less in mind, the fewer errors.
For initialization: See patterns in skills/designing-before-coding for thinking about data initialization early in design
For naming: See skills/naming-variables - short-lived local variables can have shorter names; longer-lived variables need more descriptive names