Install
$ agentstack add skill-morzecrew-agent-skills-never-nesting ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Never Nesting
Each level of indentation is one more condition the reader must hold as true to understand the innermost line. SonarSource's cognitive-complexity metric formalizes this: every control structure costs +1, plus +1 for each level of nesting it sits under. Four sequential guard clauses cost 4; the same logic as four nested ifs costs 1+2+3+4 = 10. Nesting doesn't add difficulty linearly — it compounds. The Linux kernel style guide puts it bluntly: "if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program."
Treat three levels as a soft ceiling and a fourth as a refactoring signal, not a formatting problem. The moves below flatten code; the last section covers the cases where flattening makes code worse.
Use this skill when
- Writing a function that is growing nested
if/for/tryblocks. - Refactoring arrow code / pyramid-of-doom conditionals (
if { if { if { ... }}}). - The happy path sits several indents deep with error handling wrapped around it.
- Reviewing code for readability or a complexity-metric violation.
- The user mentions nesting, guard clauses, early returns, or flattening conditionals.
Do not use this skill when
- The user explicitly wants the existing structure preserved.
- Both branches of a conditional are equally normal behavior (see "When not to flatten").
- Manual resource cleanup makes early returns leak (see "When not to flatten").
The move-set
| Move | Use when | Effect | | --- | --- | --- | | Guard clause (invert + early return) | edge/error cases wrap the happy path | happy path drops to base indent | | continue / break | per-item conditions nest a loop body | loop body flattens | | Extract function | a nested block is a coherent, nameable unit | resets nesting to zero; names intent | | Define errors out of existence | every caller must check or catch | the error branch disappears entirely | | Aggregate error handling | try/catch wraps each individual call | one handler at one level | | Dispatch table / pattern match | nested if/else selects among cases | branching becomes data |
Guard clauses: invert, fail fast, return early
Fowler's "Replace Nested Conditional with Guard Clauses": handle each unusual case first with an immediate return/raise, so the remaining code needs no else and the real work sits at base indentation. The guards read as a declaration of the function's preconditions.
# Before: reader holds 3 conditions to understand the core 3 lines
def save(user, payload):
if user is not None:
if user.is_active:
if payload.is_valid():
record = build_record(payload)
store(record)
return record
else:
raise InvalidPayload()
else:
raise InactiveUser()
else:
raise MissingUser()
# After: each condition is discharged, then forgotten
def save(user, payload):
if user is None:
raise MissingUser()
if not user.is_active:
raise InactiveUser()
if not payload.is_valid():
raise InvalidPayload()
record = build_record(payload)
store(record)
return record
Mechanics: (1) negate the outermost condition and exit early in that branch; (2) the else is now dead — delete it and promote its body one level; (3) repeat inward until the happy path is flat. Inside loops, the same inversion uses continue (or break) instead of return:
for item in items:
if item.skip:
continue
process(item)
If several guards share the same failure response, one combined condition (if not (a and b and c): raise ...) can beat three separate guards — choose whichever states the requirement most directly.
Extraction: name the block, reset the depth
When a nested block does one coherent thing, pull it into a function. This removes indentation at the call site, gives the block a name that documents intent, and — because complexity metrics assess each function separately — resets the nesting count to zero inside the new function.
# Before: the interesting logic starts 4 levels deep
def process_downloads(downloads):
for d in downloads:
if d.state == "in_progress":
result = d.process()
if result.is_error():
if result.retriable and d.retries < 3:
d.retries += 1
d.state = "pending"
else:
fail(d)
# After: the loop is a summary; the extracted function is guard-flat
def process_downloads(downloads):
for d in downloads:
if d.state == "in_progress":
handle_result(d, d.process())
def handle_result(d, result):
if not result.is_error():
return
if result.retriable and d.retries < 3:
d.retries += 1
d.state = "pending"
return
fail(d)
For a long function with distinct phases, extract each phase so the top level reads as an outline. Real refactors alternate the moves: extract to drop a level, then invert inside the extraction.
Flatten error handling at the source
Nesting is often not a control-flow problem but an API-design problem. Two moves from Ousterhout's A Philosophy of Software Design:
- Define errors out of existence. Redesign the operation so the "error"
case is normal behavior and the branch vanishes for every caller. Deleting a missing key throws → make deletion idempotent (succeed if already absent). Out-of-range substring throws (Java) → clamp to the valid range (Python slicing). One API change deletes a try/except from every call site.
- Aggregate exception handling. Instead of wrapping each call in its own
try/catch (pyramids of handlers), let exceptions propagate to a single handler at the level that can actually respond — a request-level error handler, a per-item try around the loop body, a top-level retry loop.
Replace conditional trees with data or idioms
- A nested
if/elsechain that maps a value to behavior is a **dispatch
table**: handlers[kind](payload) — branching becomes a data lookup.
- Prefer built-in flat forms where the language has them: pattern matching
(match/switch with cases), comprehensions and filter/map for filter-inside-loop, with/using for acquire-release nesting.
When not to flatten
- Symmetric branches. A guard clause signals "this branch is not what the
function is about." Fowler's rule: use guards when one branch is the unusual case; when both branches are normal behavior (days = 366 if leap else 365, buy vs sell), keep if/else (or a conditional expression) so both get equal emphasis — a guard would falsely mark one as an error path.
- Manual cleanup languages. Early return is safe only when cleanup runs
automatically: RAII destructors (C++/Rust), defer (Go), context managers (with), try/finally, using (C#). In C-style code with manual free/unlock, extra returns leak resources; the flat idiom there is a single goto err cleanup chain (the Linux kernel's own pattern) — don't graft early returns onto it.
- Trivial one-off extraction. Don't extract a two-line block used once if
the name adds nothing; a guard clause alone is often enough. Each extraction also adds a definition the reader may have to chase.
- Single-exit codebases. If the style guide bans early returns, flatten by
extraction and dispatch tables instead of inversion.
The goal is fewer conditions held in the reader's head, not a zero-indent contest.
Quick checklist
- Any function past ~3 levels? It's a candidate.
- Can edge cases become guards so the happy path reaches base indent?
- Is a nested block a nameable unit? Extract it.
- Is the nesting caused by an API that throws where it could tolerate? Fix the API.
- Is any branch you're about to guard actually normal behavior? Keep
if/else. - Does an early return skip manual cleanup? Use the language's cleanup idiom first.
Related skills
naming-things— extraction only pays off if the new function's name informs; naming difficulty means the block isn't a coherent unit yet.self-documenting-code— guard clauses and extracted functions document intent that comments would otherwise carry.less-code-same-behavior— flattening often reveals duplicate branches that can be merged or deleted.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: morzecrew
- Source: morzecrew/agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.