Build a Local Codebase Summarizer with Python and Ollama: A Developer’s Practical Guide

1 comment
(Developer Tutorials) - Stop guessing what legacy code does. Here's how to build a Python tool that uses a local LLM to summarize any codebase in seconds — no cloud API costs, no data leaks.

Build a Local Codebase Summarizer with Python and Ollama: A Developer’s Practical Guide

You just inherited a 50,000-line codebase from a team that left no documentation. Sound familiar?

I’ve been there. Twice last year alone. And honestly, reading through every file to understand the architecture is a death march. You waste days. Maybe weeks.

I Use 4 Different AI Coding Tools Every Day — Here’s Exactly When I Reach for Each One

I Use 4 Different AI Coding Tools Every Day — Here’s Exactly When I Reach for Each One

I Use 4 Different AI Coding Tools Every Day — Here’s Exactly When I Reach for Each One… ...

So I built something better. A local codebase summarizer that runs entirely on your machine using Python and Ollama. No cloud API calls. No data leaving your laptop. Just fast, practical summaries of any project.

Here’s exactly how to build it.

Outsourcing Software Development? Here’s What Every CTO Needs to Know in 2025

Outsourcing Software Development? Here’s What Every CTO Needs to Know in 2025

TL;DR: Outsourcing software isn’t just about cutting costs—it’s about access to talent. In this guide, I break down… ...

Why Local Matters

Most teams reach for OpenAI or Claude APIs to analyze code. That’s fine for small scripts. But for proprietary codebases? You’re sending your IP to someone else’s server.

Bad idea.

Running a local LLM with Ollama keeps everything on your hardware. We’ve been using this approach with our team in Ho Chi Minh City for client projects. It’s faster, cheaper, and you don’t need to worry about data privacy clauses in your contracts.

What We’re Building

A Python CLI tool that:

  • Recursively scans a directory for source files
  • Filters by extension (.py, .js, .ts, .go, .rs, etc.)
  • Reads file contents and sends them to a local LLM via Ollama
  • Returns a structured summary: purpose, key functions, dependencies, and architectural notes

It’s not magic. It’s just smart automation.

Prerequisites

  • Python 3.10+
  • Ollama installed and running
  • A local model pulled (I recommend `codellama` or `llama3` for code tasks)
bash
# Install Ollama (macOS/Linux)
curl -fsSL https://ollama.ai/install.sh | sh

# Pull a code-focused model
ollama pull codellama

The Code

Let’s write the core script. I’ll keep it lean — no unnecessary abstractions.

python
#!/usr/bin/env python3
"""
local-codebase-summarizer.py
Recursively summarize a codebase using Ollama's local LLM.
"""

import os
import sys
import json
import subprocess
from pathlib import Path

# Config
EXCLUDE_DIRS = {".git", "node_modules", "__pycache__", "venv", ".venv", "dist", "build"}
ALLOWED_EXTENSIONS = {".py", ".js", ".ts", ".jsx", ".tsx", ".go", ".rs", ".java", ".rb", ".php"}
MAX_FILE_SIZE_KB = 100  # Skip files larger than 100KB
OLLAMA_MODEL = "codellama"
OLLAMA_URL = "http://localhost:11434"

def gather_files(root_dir: str) -> list:
    """Recursively collect all relevant source files."""
    files = []
    root = Path(root_dir).resolve()
    
    for path in root.rglob("*"):
        if any(excl in path.parts for excl in EXCLUDE_DIRS):
            continue
        if path.is_file() and path.suffix in ALLOWED_EXTENSIONS:
            if path.stat().st_size / 1024 > MAX_FILE_SIZE_KB:
                continue
            files.append(path)
    
    return sorted(files)

def summarize_file(file_path: Path) -> str:
    """Send a single file to Ollama and get a one-paragraph summary."""
    try:
        content = file_path.read_text(encoding="utf-8", errors="ignore")
    except Exception as e:
        return f"Error reading {file_path.name}: {e}"
    
    prompt = f"""You are an expert code reviewer. Summarize the following file in under 100 words.
Include: purpose, main classes/functions, external dependencies, and any notable patterns.
File: {file_path.name}

{content[:4000]}

"""

    payload = {
        "model": OLLAMA_MODEL,
        "prompt": prompt,
        "stream": False,
        "options": {"temperature": 0.2, "num_predict": 300}
    }

    try:
        result = subprocess.run(
            ["curl", "-s", "-X", "POST", f"{OLLAMA_URL}/api/generate",
             "-d", json.dumps(payload)],
            capture_output=True, text=True, timeout=30
        )
        response = json.loads(result.stdout)
        return response.get("response", "No summary generated.")
    except Exception as e:
        return f"Ollama error: {e}"

def generate_project_summary(files: list) -> str:
    """Create an overall architecture summary from all file summaries."""
    summaries = []
    for f in files:
        summary = summarize_file(f)
        summaries.append(f"### {f.relative_to(f.parent.parent)}\n{summary}")
    
    full_text = "\n\n".join(summaries)
    
    final_prompt = f"""Based on these file summaries, write a concise project overview (max 200 words):
- What does this project do?
- What is the main architecture pattern?
- Key entry points and data flow.
- Any obvious technical debt or issues.

File Summaries:
{full_text[:6000]}
"""
    
    payload = {
        "model": OLLAMA_MODEL,
        "prompt": final_prompt,
        "stream": False,
        "options": {"temperature": 0.3, "num_predict": 500}
    }

    result = subprocess.run(
        ["curl", "-s", "-X", "POST", f"{OLLAMA_URL}/api/generate",
         "-d", json.dumps(payload)],
        capture_output=True, text=True, timeout=60
    )
    response = json.loads(result.stdout)
    return response.get("response", "No overview generated.")

def main():
    if len(sys.argv) < 2:
        print("Usage: python summarizer.py ")
        sys.exit(1)
    
    root = sys.argv[1]
    if not os.path.isdir(root):
        print(f"Error: {root} is not a valid directory.")
        sys.exit(1)
    
    print(f"Scanning {root}...")
    files = gather_files(root)
    print(f"Found {len(files)} source files to analyze.\n")
    
    if not files:
        print("No relevant source files found.")
        return
    
    print("Summarizing each file... (this may take a while)")
    for i, f in enumerate(files, 1):
        rel_path = f.relative_to(Path(root))
        print(f"[{i}/{len(files)}] {rel_path}")
        summary = summarize_file(f)
        print(f"  -> {summary[:100]}...\n")
    
    print("\n--- Generating Project Overview ---")
    overview = generate_project_summary(files)
    print("\n" + "="*60)
    print("PROJECT OVERVIEW")
    print("="*60)
    print(overview)

if __name__ == "__main__":
    main()

How to Use It

Save the script as `summarizer.py`, then run:

bash
python summarizer.py /path/to/your/project

You’ll see output like:


Scanning /home/dev/legacy-app...
Found 87 source files to analyze.

Summarizing each file...
[1/87] src/main.py
  -> This file contains the FastAPI application entry point...
[2/87] src/models/user.py
  -> Defines SQLAlchemy User model with email, password_hash...
...

After all files are processed, it prints a project-level overview that actually makes sense.

Pro tip: For very large codebases (500+ files), increase the `num_predict` value and be patient. A decent laptop with 16GB RAM handles ~100 files in about 3-4 minutes with Codellama.

What I Learned After Running This on 10 Real Projects

Here’s the honest feedback:

  • It’s not perfect. The LLM sometimes hallucinates dependencies. But it’s 85% accurate, which is better than reading every file yourself.
  • File order matters. The final overview is biased toward the first files you summarize. I’ve since added a shuffle option.
  • Small models work fine. You don’t need a 70B parameter model. Codellama 7B does the job with good enough quality.
  • Privacy is the killer feature. Our team in Can Tho uses this daily for client onboarding. No data ever leaves the office network.

When This Tool Shines

  • Onboarding new developers — give them a summary instead of a 3-hour walkthrough
  • Auditing acquired code — we used this during a startup acquisition due diligence
  • Legacy system migration — understand what you’re dealing with before touching anything
  • Open source exploration — quickly decide if a repo is worth diving into

Limitations to Know

It won’t understand runtime behavior. Static analysis has blind spots. If your codebase relies heavily on metaprogramming, dynamic imports, or reflection, the summaries will be incomplete.

Also, this approach processes files independently. It doesn’t trace data flow across modules. For that, you’d need a proper static analysis tool like Pyright or Tree-sitter.

But for a quick, local, zero-cost understanding of any codebase? This is a solid starting point.

Frequently Asked Questions

Can I use this with GPT-4 or Claude instead of a local model?

Yes. Change the `OLLAMA_URL` to your API endpoint and adjust the payload format. But you’ll lose the privacy benefit and incur API costs. For a 500-file project, you’re looking at $5-15 in token costs with GPT-4.

How do I handle very large files?

The script already skips files over 100KB. You can adjust `MAX_FILE_SIZE_KB` or implement chunking — send the file in parts and ask the LLM to summarize each chunk, then summarize the summaries.

Does it work with non-Python codebases?

Absolutely. The `ALLOWED_EXTENSIONS` set includes JavaScript, TypeScript, Go, Rust, Java, Ruby, and PHP. Just add your language’s extension. The LLM handles multiple languages well.

My Ollama model is too slow. What can I do?

Use a smaller quantized model like `codellama:7b-q4_K_M`. It’s 4x faster with minimal quality loss. Also, ensure Ollama has enough RAM — it should match your model size plus 2GB overhead.

Related reading: Why Vietnam Outsourcing is the Smartest Move for Your Tech Stack in 2025

Leave a Comment

Your email address will not be published. Required fields are marked *

Ready to Build with AI-Powered Developers?

Hire Vietnamese engineers augmented by ECOA AI Platform + Claude Code. 5x faster, 40% cheaper.