DEV Community

shashank ms
shashank ms

Posted on

Debugging Complex Code: Strategies and Techniques

I recently shipped an internal CLI tool that reads Python tracebacks and proposes patches automatically. It runs on Oxlo.ai, where request-based pricing means I can paste in thousand-line stack traces without counting tokens. In this tutorial, I will walk you through building the same agent from scratch.

What you'll need

Step 1: Scaffold the debug agent

I start by importing the OpenAI SDK and pointing it at Oxlo.ai. Because the platform is fully OpenAI API compatible, this is a drop-in replacement.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

Step 2: Craft the system prompt

The system prompt constrains the model to emit a root cause analysis followed by a unified diff. I keep it strict so the output is machine-parseable.

SYSTEM_PROMPT = """You are an expert debugging agent. Your job is to analyze a Python error trace and the corresponding source file, then produce a precise fix.

Rules:
1. First, explain the root cause in one paragraph.
2. Then, provide a unified diff patch that can be applied directly to the file.
3. Wrap the diff inside triple backticks with the language tag 'diff'.
4. Do not change unrelated code. Keep the fix minimal.
"""

Step 3: Ingest trace and source

Next, I need a helper that reads the offending file from disk and formats the user message. I include both the full source and the traceback so the model has complete context.

import os

def build_debug_message(file_path: str, error_trace: str) -> str:
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"{file_path} not found")
    
    with open(file_path, "r", encoding="utf-8") as f:
        source = f.read()
    
    ext = os.path.splitext(file_path)[1].lstrip(".") or "text"
    
    return f"""File: {file_path}
Source:


```{ext}
{source}
```



Traceback:


```plaintext
{error_trace}
```



Analyze the traceback and source, then provide the root cause and a minimal diff patch.
"""

Step 4: Run the inference loop

Now I wire the message into a chat completion call. I use deepseek-v3.2 because it handles code and reasoning well, and Oxlo.ai serves it with no cold starts.

def run_debug_agent(file_path: str, error_trace: str) -> str:
    user_message = build_debug_message(file_path, error_trace)

    response = client.chat.completions.create(
        model="deepseek-v3.2",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
    )

    return response.choices[0].message.content

Step 5: Parse and apply patches

The model returns markdown. I extract the diff block with a regex and write it to a proposed patch file. I do not auto-apply patches blindly, but I make them easy to review.

import re

def extract_diff(agent_output: str) -> str | None:
    match = re.search(r"

```diff\n(.*?)\n```

", agent_output, re.DOTALL)
    return match.group(1) if match else None

def save_patch(file_path: str, diff_text: str) -> None:
    patch_path = file_path + ".proposed.patch"
    with open(patch_path, "w", encoding="utf-8") as f:
        f.write(diff_text)
    print(f"Patch saved to {patch_path}")

Run it

Here is a complete script that writes a buggy parser.py, feeds the agent the traceback, and prints the result. I also show the kind of output you can expect.

if __name__ == "__main__":
    # Write a deliberately buggy file
    buggy_code = '''import re

def parse_log_line(line):
    pattern = r"(\\d{4}-\\d{2}-\\d{2}) (\\w+) (.*)"
    match = re.match(pattern, line)
    return {
        "date": match.group(1),
        "level": match.group(2),
        "message": match.group(3)
    }

lines = [
    "2024-01-15 ERROR disk full",
    "malformed line without date",
]

for line in lines:
    print(parse_log_line(line))
'''
    with open("parser.py", "w", encoding="utf-8") as f:
        f.write(buggy_code)

    # Traceback captured from running the buggy file
    trace = '''Traceback (most recent call last):
  File "parser.py", line 16, in <module>
    print(parse_log_line(line))
  File "parser.py", line 7, in parse_log_line
    "date": match.group(1),
AttributeError: 'NoneType' object has no attribute 'group'
'''

    output = run_debug_agent("parser.py", trace)
    print("=== AGENT OUTPUT ===")
    print(output)

    diff = extract_diff(output)
    if diff:
        save_patch("parser.py", diff)

Example output:

=== AGENT OUTPUT ===
The root cause is that `re.match` returns `None` when the line does not match the pattern, but the code immediately calls `.group()` on the result without checking for a match.



```diff
--- parser.py
+++ parser.py
@@ -4,7 +4,10 @@
 def parse_log_line(line):
     pattern = r"(\d{4}-\d{2}-\d{2}) (\w+) (.*)"
     match = re.match(pattern, line)
+    if not match:
+        return {"date": None, "level": None, "message": line}
     return {
         "date": match.group(1),
         "level": match.group(2),
```



Patch saved to parser.py.proposed.patch

Wrap-up and next steps

You now have a working debugging agent that ingests tracebacks and proposes concrete diffs. To make it production ready, wire it into a Git pre-commit hook so it triggers on test failures, or extend the prompt to request unit tests that reproduce the bug before applying the fix.

Top comments (0)