I Tried Solving Problems With Only Recursion in Python — It Got Weird

Recursion is beautiful, powerful, and… occasionally painful. I challenged myself to solve everyday problems using only recursion in Python.

I Tried Solving Problems With Only Recursion in Python — It Got Weird
Photo by Sander Sammy on Unsplash

Recursive thinking sounds elegant — until it becomes your only option.

I Tried Solving Problems With Only Recursion in Python — It Got Weird

We’ve all learned recursion at some point: a function calling itself until it reaches a base case. Simple in theory, intimidating in practice.

Most tutorials stick to classic examples — Fibonacci, factorials, maybe a tree traversal if you’re feeling spicy. But I wanted to go deeper.

So I gave myself a challenge:
For one week, I would solve all programming problems using only recursion. No loops allowed.

What started as a fun brain teaser soon tested my patience, logic, and understanding of Python’s call stack.

This is what I learned.

Why Recursion Is (Usually) Not Your First Choice in Python

Before diving in, here’s a truth Python programmers quickly learn:
Python isn’t built for deep recursion.

Unlike languages like Scheme or Haskell, Python has a default recursion limit (sys.getrecursionlimit()) of 1000. And it doesn't optimize for tail recursion — so forget about that escape hatch.

Here’s what that means:

  • Deep recursion? Likely to crash with a RecursionError.
  • Infinite recursion? Game over.
  • Tail call optimization? Not in Python’s toolbox.

So the real challenge wasn’t just logic. It was fighting the interpreter itself.

The Rules of the Challenge

To keep things fair (and painful), I set a few rules:

  • No for or while loops
  • No external libraries that abstract loops
  • Recursion only
  • Must solve problems from real-world categories: strings, lists, sorting, etc.
  • Readable code preferred (not just code golf)

And yes — I committed to only using pure recursion, meaning no iterative helpers or generators.

Problem #1: Sum of a List

First up, a warm-up.

def recursive_sum(lst): 
    if not lst: 
        return 0 
    return lst[0] + recursive_sum(lst[1:])

It worked perfectly.
Recursion feels elegant when you’re not dealing with large input sizes. But I already felt the itch to just write sum(lst) or for num in lst.

Problem #2: Reversing a String

def reverse_string(s): 
    if s == "": 
        return s 
    return reverse_string(s[1:]) + s[0]

Again, small inputs? No problem.

But with longer strings, it’s painfully inefficient. Each concatenation creates a new string — and recursion adds overhead.
For 10,000 characters? Expect trouble.

Still, the logic was clean, and honestly… fun.

Problem #3: Flattening a Nested List

Here’s where things got tricky.

def flatten(lst): 
    if not lst: 
        return [] 
    if isinstance(lst[0], list): 
        return flatten(lst[0]) + flatten(lst[1:]) 
    return [lst[0]] + flatten(lst[1:])

This is where recursion shines. No loop could match the elegance of this deeply nested traversal.
Win for recursion.

But performance? You’ll start feeling it. Python doesn’t like excessive stack frames.

Problem #4: FizzBuzz

Ah yes — the classic.

def fizzbuzz(n): 
    if n > 100: 
        return 
    if n % 3 == 0 and n % 5 == 0: 
        print("FizzBuzz") 
    elif n % 3 == 0: 
        print("Fizz") 
    elif n % 5 == 0: 
        print("Buzz") 
    else: 
        print(n) 
    fizzbuzz(n + 1)

This was surprisingly fun to write recursively.

But it felt… forced.
A loop is just more readable and expected here.

Verdict: Recursion can do it, but should it?

Problem #5: Sorting With Merge Sort

Now this was satisfying.

def merge_sort(arr): 
    if len(arr) <= 1: 
        return arr 
    mid = len(arr) // 2 
    left = merge_sort(arr[:mid]) 
    right = merge_sort(arr[mid:]) 
    return merge(left, right) 
 
def merge(left, right): 
    if not left: 
        return right 
    if not right: 
        return left 
    if left[0] < right[0]: 
        return [left[0]] + merge(left[1:], right) 
    else: 
        return [right[0]] + merge(left, right[1:])

This is where recursion belongs.

Divide and conquer algorithms like Merge Sort thrive with recursion.

This was the first time the recursive approach felt natural and performant (within reasonable input sizes).

Problem #6: Generating All Subsets

Here’s a classic recursive combinatorics problem:

def subsets(nums): 
    if not nums: 
        return [[]] 
    rest = subsets(nums[1:]) 
    return rest + [[nums[0]] + item for item in rest]

This recursive gem is elegant, expressive, and much harder to express with iteration.

Score another for recursion.

The Ugly Side: When Recursion Backfires

Here’s the thing: recursion isn’t just about writing code that works — it’s about writing code that’s safe and scalable.

Where recursion hurt:

  • String concatenation inside recursion
    → memory killer
  • Large lists
    → fast route to a RecursionError
  • Debugging
    → good luck tracing stack frames 30 levels deep
  • Performance
    → unless it’s divide-and-conquer, recursion tends to lag

Key Takeaways From My Recursion-Only Week

By forcing recursion, I learned a lot about Python’s internals — and myself.

What recursion taught me:

  • Elegance comes at a cost
    Recursion looks clean… until you crash the stack.
  • Use recursion for the right problems
    Trees, graphs, combinatorics, divide-and-conquer. Not FizzBuzz.
  • Python isn’t a recursion-first language
    And that’s okay. Knowing when not to use recursion is just as valuable.
  • You start thinking differently
    Solving problems recursively builds pattern recognition for problem decomposition.

Would I Do It Again?

Yes — but not for production code.

Recursion-only challenges are great for sharpening your thinking, improving interviews, and appreciating problem structure.

But in real-world code?
Use the right tool for the job. Loops exist for a reason. So do stacks, generators, and vectorized operations.


Final Thoughts

Recursion is a beautiful tool — but it’s not a Swiss Army knife.
It thrives in niche domains and flounders elsewhere.

After a week of pure recursion, I’m both in awe of its power and reminded of its limits.

And honestly?
Next time I need to sum a list, I’m reaching for a loop.

Photo by Jessica Pamp on Unsplash