Programming AI, not prompting it

January 29, 2026·6 min read

AILLMsDSPyLangChainEngineering

I spent an embarrassing amount of time last year tweaking prompt strings. Not five minutes here and there, hours. "Maybe if I add one more adjective," I'd think. "Perhaps reordering these instructions will help." I'd test with a few examples, notice the 2% improvement, and celebrate like I'd solved climate change. It was inefficient and brittle. It was the opposite of how I actually build software (or liked building).

I discovered DSPy, and it felt like someone finally said the quiet part out loud: we've been doing this backward.

Here's what drives me crazy about "prompt engineering": it's not engineering. It's configuration. You take a text file, add emojis, paste in examples, adjust the temperature, and hope. When something breaks, you crack open that same text file and hope harder. Your "AI system" is now a collection of string templates with implicit dependencies. Good luck maintaining that in production. You optimize prompts for one model, then Claude 4.5 comes out and half your work evaporates. You need a new model for faster inference? Rewrite everything. You want to compose multiple steps together? Now you're playing Jenga with interdependent prompts.

DSPy stops this madness. It says: stop tweaking strings, start writing code.

What DSPy Actually Does

DSPy is a framework for programming language models instead of prompting them. That distinction matters more than it sounds.

In traditional development, you'd write: result = function(input). The function is deterministic, repeatable, testable. With DSPy, you write code that looks exactly like that, but the function itself is powered by an LLM. More importantly, you define what the function should do (a signature), what technique to use (a module), and then let the framework optimize it automatically.

Think of it like this: you describe the problem structure, compose modules to solve it, and then the system tunes the prompts and examples for you. No manual tweaking required. It's declarative, you declare what you want, not how to prompt for it.

The framework has three core pieces:

  1. Signatures: A declaration of inputs and outputs. "Given research feedback, extract three key insights."
  2. Modules: Techniques for solving problems. Chain-of-thought, ReAct, multi-hop reasoning—all composable.
  3. Optimizers: Algorithms that automatically improve your modules by adjusting instructions, generating few shot examples, or even finetuning weights.

A practical example: Building a multi-stage analysis pipeline

Let me show you something concrete. Imagine you're building a system that processes customer research feedback and extracts actionable insights. This is something I hadn't done before, and it's a perfect DSPy use case because it naturally breaks into multiple stages.

I a traditional approach you'd write a monster prompt that tries to do everything at once. In DSPy you build modules, compose them, and let the optimizer handle the rest.

What just happened?

  1. You defined structure, not prompts. The signatures tell DSPy what the inputs and outputs are—clean, declarative, self-documenting.

  2. You composed multiple steps. Each module does one thing well. The pipeline is readable. Testable. You could swap out modules independently.

  3. The system optimized itself. You gave it a metric (what "good" means) and training examples, and the optimizer tuned the internal prompts and few-shot examples automatically.

  4. It stays portable. If you need to switch from Claude to another model? Update one line. The module structure doesn't change. The signatures don't change.

VS LangChain

LangChain is a mature framework that does something superficially similar. If I were building the same pipeline in LangChain, it would look like this:

LangChain's strength: It's excellent for orchestrating complex workflows with tools, agents, and memory. It's mature, battle tested, and solves real production problems. If you need to build a system with RAG, tool calling, and agent loops, LangChain is the pragmatic choice.

DSPy's strength: It solves the prompt engineering problem at its core. You don't optimize manually, the system does it for you. This is powerful if you care about maintainability, portability, and not wasting time tweaking strings.

The key difference: LangChain assumes you'll write good prompts and compose them well. DSPy assumes you'll define the problem structure and let the system optimize. One is about better orchestration. The other is about removing orchestration entirely.

In the LangChain version, when the model's performance drifts, I'm back to tweaking template strings. In the DSPy version, I define a metric and let the optimizer run. That's the fundamental philosophical difference.

I've been building software for a long time. I've seen frameworks and tools come and go. What I appreciate is when something respects the principles of good engineering: modularity, composability, testability, maintainability.

DSPy does that. It treats AI systems like actual systems, not like configuration files.

Is DSPy a replacement for LangChain? In my opinion, it is not. They solve different problems. DSPy represents a genuinely different way of thinking about LLM applications. And after a year of tweaking prompt strings, that felt pretty nice.