← Back to DevBytes

Aider: Pair Programming with Local LLMs

Introduction to Aider and Local LLMs

Aider is an open-source AI pair programming tool that runs in your terminal and edits code directly in your local Git repository. While many developers associate Aider with cloud-hosted models like GPT-4 or Claude, it also supports local LLMs through providers such as Ollama, LM Studio, and llama.cpp. Pairing Aider with a local model gives you a fully offline, privacy-preserving coding assistant that can read, modify, and refactor your codebase without sending a single byte to a third-party server.

This tutorial walks through everything you need to know to set up Aider with a local LLM, configure it for best results, and adopt workflows that make the most of on-device pair programming.

Why Pair Programming with Local LLMs Matters

Running your AI coding assistant locally offers several compelling advantages:

The trade-off is that local models are typically smaller and less capable than frontier cloud models. However, modern 7B–34B parameter coding models like Qwen2.5-Coder, DeepSeek-Coder, and Codestral are surprisingly effective for everyday refactoring, test generation, and bug fixing when paired with Aider's structured editing workflow.

Prerequisites and Installation

Installing Aider

Aider is a Python package. Install it with pip or pipx (recommended for isolated environments):

# Using pipx (recommended)
pipx install aider-chat

# Or with pip
pip install aider-chat

# Verify installation
aider --version

Installing a Local LLM Runtime

The easiest way to run a local LLM is Ollama, which exposes an OpenAI-compatible API on localhost:11434. Install it from the official site, then pull a coding-focused model:

# Pull a strong coding model
ollama pull qwen2.5-coder:7b

# Or a larger variant if you have the VRAM
ollama pull qwen2.5-coder:32b

# Verify it runs
ollama run qwen2.5-coder:7b "Say hello"

Alternative runtimes like LM Studio or llama.cpp also work, as long as they expose an OpenAI-compatible endpoint.

Connecting Aider to Your Local Model

Aider uses LiteLLM under the hood, which means you reference local models with an ollama/ prefix. Launch Aider inside a Git repository and point it at your local model:

cd my-project
aider --model ollama/qwen2.5-coder:7b

If you are using LM Studio, start its local server (default port 1234) and connect like this:

aider --model openai/your-model-name \
  --openai-api-base http://localhost:1234/v1 \
  --openai-api-key dummy

You can also persist configuration by creating an .aider.conf.yml file in your home directory or project root:

# .aider.conf.yml
model: ollama/qwen2.5-coder:7b
auto-commits: true
dark-mode: true

Basic Pair Programming Workflow

Once Aider launches, you are dropped into an interactive chat. Aider automatically tracks which files you add to the chat, sends their contents to the model, and applies the model's proposed edits directly to disk. Every change is committed to Git with a descriptive message.

Adding Files to the Chat

# Inside the Aider prompt
/add src/auth.py src/auth_test.py

Requesting a Change

# Ask for a refactor
Refactor the login function to use async/await and add input validation.

Aider will respond with a diff, apply it to src/auth.py, and commit the change. You can review the commit with git log or git show HEAD.

Useful In-Chat Commands

Working with Larger Codebases

Local models have smaller effective context windows than cloud models, so context management is critical. Aider provides a repository map feature that builds a compact summary of your entire codebase using tree-sitter. This lets the model "see" the structure of files it has not been explicitly given.

# Enable a repo map with a token budget
aider --model ollama/qwen2.5-coder:7b \
      --map-tokens 1024 \
      --map-refresh auto

For very large projects, consider disabling the repo map and adding only the specific files relevant to your task:

aider --model ollama/qwen2.5-coder:7b --no-map

Practical Example: Adding a Feature

Suppose you have a small Flask app and want to add rate limiting. Here is how a typical session looks.

Project structure:

my-app/
├── app.py
├── requirements.txt
└── tests/
    └── test_app.py

Launch Aider and add the relevant files:

$ cd my-app
$ aider --model ollama/qwen2.5-coder:7b

Aider v0.x.x
Model: ollama/qwen2.5-coder:7b
Git repo: .git with 12 files

> /add app.py tests/test_app.py
Added app.py to the chat.
Added tests/test_app.py to the chat.

> Add a simple in-memory rate limiter that allows
  at most 60 requests per minute per client IP.
  Apply it to all routes. Also add tests for it.

Aider will edit app.py to introduce a rate limiter, update tests/test_app.py with new test cases, and commit each logical change. A typical generated implementation might look like:

# app.py
from flask import Flask, request, jsonify
from collections import defaultdict
import time

app = Flask(__name__)

RATE_LIMIT = 60
RATE_WINDOW = 60  # seconds
request_log = defaultdict(list)


def rate_limited():
    now = time.time()
    ip = request.remote_addr
    request_log[ip] = [t for t in request_log[ip] if now - t < RATE_WINDOW]
    if len(request_log[ip]) >= RATE_LIMIT:
        return jsonify({"error": "rate limit exceeded"}), 429
    request_log[ip].append(now)
    return None


@app.before_request
def enforce_rate_limit():
    response = rate_limited()
    if response:
        return response


@app.route("/")
def index():
    return jsonify({"message": "hello"})


if __name__ == "__main__":
    app.run(debug=True)

And the corresponding tests:

# tests/test_app.py
import pytest
from app import app


@pytest.fixture
def client():
    app.config["TESTING"] = True
    with app.test_client() as c:
        yield c


def test_index_ok(client):
    r = client.get("/")
    assert r.status_code == 200


def test_rate_limit_enforced(client):
    for _ in range(60):
        assert client.get("/").status_code == 200
    assert client.get("/").status_code == 429

Run the tests to verify:

pytest -q

Best Practices for Local LLM Pair Programming

Choose the Right Model Size

Match the model to your hardware. As a rough guide:

Keep Tasks Small and Focused

Local models struggle with long, multi-step instructions. Break work into small, verifiable chunks: one feature, one bug, or one test at a time. Use /clear between unrelated tasks to avoid context drift.

Use Git as Your Safety Net

Aider auto-commits every change, which means you can always git revert or /undo a bad edit. Review diffs before accepting them:

git diff HEAD~1

Provide Explicit Context

Unlike frontier models, local models benefit from explicit instructions. Mention file names, function names, and expected behavior directly in your prompt. For example:

In src/payment.py, rename process_payment() to charge_card()
and update all callers in src/api.py and src/webhooks.py.
Keep the function signature the same.

Use Architect Mode for Complex Tasks

Aider supports an "architect" mode where one model designs a plan and another (the "editor") applies edits. You can use a stronger local model as the architect and a smaller one as the editor:

aider --model ollama/qwen2.5-coder:7b \
      --architect \
      --editor-model ollama/qwen2.5-coder:7b

Watch Token Usage

Run /tokens periodically to ensure you are not exceeding your model's context window. If you are, drop files with /drop or reduce --map-tokens.

Troubleshooting Common Issues

Conclusion

Aider combined with a local LLM gives you a capable, private, and cost-effective pair programmer that lives entirely on your machine. By choosing a coding-tuned model like Qwen2.5-Coder, keeping tasks focused, leveraging Aider's repository map, and relying on Git as a safety net, you can integrate AI-assisted development into your workflow without depending on any external API. As local models continue to improve, this offline-first approach is becoming a practical and sustainable option for everyday software development.

— Ad —

Google AdSense will appear here after approval

← Back to all articles