Skip to main content

Code Quality

info

Enable quality checks: skylos . --quality

Why Quality Metrics Matter

Complex code isn't just hard to read—it's statistically more likely to contain bugs.

Research shows that functions with complexity above 10 have significantly higher defect rates. At 20+, the function is nearly impossible to test exhaustively.

ComplexityRisk LevelDescription
1-10Low riskEasy to understand, test, and maintain
11-20Moderate riskConsider refactoring when touching this code
21+High riskLikely contains bugs, very hard to test

Cyclomatic Complexity (SKY-Q301)

Cyclomatic complexity counts the number of independent paths through a function. Each decision point adds a path.

What Adds Complexity

ConstructComplexity Added
if / elif+1 per branch
for / while+1
try / except+1 per handler
with+1
and / or+1 per operator
Ternary x if y else z+1
List/dict/set comprehension+1

Example

def process_order(order): 
if not order.valid:
return None

if order.priority == 'high':
if order.value > 1000:
apply_discount(order)
notify_manager(order)
elif order.priority == 'medium':
queue_order(order)

for item in order.items:
if item.backordered:
handle_backorder(item)
elif item.fragile:
mark_fragile(item)

return order

How to Reduce Complexity

# Before: Complexity 8
def process_order(order):
if not order.valid:
return None
# ... 50 more lines of nested logic

# After: Complexity 3 each
def process_order(order):
if not order.valid:
return None
handle_priority(order)
process_items(order)
return order

def handle_priority(order):
# Isolated complexity
pass

def process_items(order):
# Isolated complexity
pass

Nesting Depth (SKY-Q302)

Deep nesting forces readers to hold multiple conditions in working memory simultaneously.

# Depth 5 - Very hard to follow
def bad_example():
if condition1: # 1st level
for item in items: # 2nd level
if condition2: # 3rd level
try: # 4th level
if condition3: # 5th level (RIP)
process(item)
except:
pass

Severity Thresholds

DepthSeverityAction
≤3OKNo issue
4-5MEDIUMConsider refactoring
6-8HIGHShould refactor
9+CRITICALMust refactor

Reducing Nesting

# Before: Depth 4
def process_users(users):
for user in users:
if user.active:
if user.verified:
if user.has_permission:
do_something(user)

# After: Depth 1 (using continue)
def process_users(users):
for user in users:
if not user.active:
continue
if not user.verified:
continue
if not user.has_permission:
continue
do_something(user)

Structure Rules

Function Length (SKY-Q303)

Long functions usually do too much. They're hard to test and understand.

[tool.skylos]
max_lines = 50 # Default
tip

Rule of thumb: If you can't see the whole function on one screen, it's probably too long.

Argument Count (SKY-Q304)

Functions with many parameters are hard to call correctly and often indicate a missing abstraction.

# Too many arguments
def create_user(name, email, age, address, phone, department, role, manager, start_date):
pass

# Group into objects
@dataclass
class UserInfo:
name: str
email: str
age: int

@dataclass
class Employment:
department: str
role: str
manager: str
start_date: date

def create_user(info: UserInfo, employment: Employment):
pass
[tool.skylos]
max_args = 5 # Default

Logic Rules

Mutable Default Arguments (SKY-L001)

One of Python's most common gotchas:

# Bug: list is shared across all calls
def append_to(item, target=[]):
target.append(item)
return target

>>> append_to(1)
[1]
>>> append_to(2)
[1, 2] # Unexpected! Same list.
# Fixed
def append_to(item, target=None):
if target is None:
target = []
target.append(item)
return target

Bare Except (SKY-L002)

Catching everything swallows KeyboardInterrupt and SystemExit:

# Too broad
try:
risky()
except: # Catches Ctrl+C, sys.exit(), everything
pass

# Specific
try:
risky()
except Exception: # Excludes BaseException subclasses
handle_error()

Identity Comparisons (SKY-L003)

Use is for singletons:

# Wrong
if value == None:
pass

# Correct
if value is None:
pass

Visual Dashboard

When you run Skylos with --quality, you get a clear breakdown:

────────────────────────── Quality Issues ──────────────────────────
# Type Function Detail Location
1 Complexity process_order McCabe=18 (target ≤10) orders.py:45
2 Nesting validate_input Depth 6 (target ≤3) validators.py:23
3 Structure generate_report 142 lines (target ≤50) reports.py:10
4 Logic create_cache Mutable default argument cache.py:5

Tip: split helpers, add early returns, flatten branches.

Configuration

[tool.skylos]
complexity = 10 # McCabe complexity threshold
nesting = 3 # Max nesting depth
max_args = 5 # Max function arguments
max_lines = 50 # Max function length

# Ignore specific rules
ignore = ["SKY-L003"] # Allow == None comparisons

Per-Language Overrides

TypeScript often has higher complexity in UI code:

[tool.skylos.languages.typescript]
complexity = 15
nesting = 4

Integrating Quality Gates

Block PRs that introduce complex code:

- name: Quality Gate
run: skylos . --quality --gate

With configuration:

[tool.skylos.gate]
max_quality = 10 # Fail if >10 quality issues

Next Steps