DEV Community

Cover image for Object-Oriented Abstraction Didn't Disappear. It Moved Into Prompts
Prasad MK
Prasad MK

Posted on • Originally published at Medium

Object-Oriented Abstraction Didn't Disappear. It Moved Into Prompts

You call car.start() and you don't care if it's a V8 or an electric motor underneath. That's abstraction: a stable interface, a swappable implementation.

Prompt engineering usually skips that. Most prompts are one long string doing the job of a method signature, a method body, and a config file at once. Swap the model and the string that worked yesterday quietly stops working today. No error, no warning, just a slightly wrong answer.

DSPy, a framework out of Stanford, applies OOP-style structure to this problem. Here's how it works.

Separate the contract from the wording

In DSPy you don't write a prompt. You write a Signature, an input/output contract with zero wording attached:

class GenerateAnswer(dspy.Signature):
    """Answer questions based on the given context."""
    context = dspy.InputField()
    question = dspy.InputField()
    answer = dspy.OutputField()
Enter fullscreen mode Exit fullscreen mode

That's the interface. Given context and a question, produce an answer. It reads like a method signature: answer(context, question) -> answer.

Then you attach a strategy that satisfies it:

class RAG(dspy.Module):
    def __init__(self):
        self.generate = dspy.ChainOfThought(GenerateAnswer)

    def forward(self, context, question):
        return self.generate(context=context, question=question)
Enter fullscreen mode Exit fullscreen mode

ChainOfThought reasons before answering. Swap it for Predict and you get a direct answer, no visible reasoning, same signature. The caller never notices. This is the same move as swapping a PostgresRepository for a MySQLRepository behind one Repository interface.

Compiling a prompt instead of writing one

This is where DSPy earns the comparison to a compiler. Give it labeled examples and a metric (did the answer match what you expected?) and it searches over prompt phrasings and few-shot sets, scores each one, keeps the best. You don't hand-write the final prompt. DSPy compiles it.

Say you tuned a prompt against GPT-4 and it performs well. Move to Claude, and that tuned wording often performs worse, because different models respond to different phrasing. Normally you retune by hand, from scratch. With DSPy, the signature stays fixed. You recompile against the new model, and the optimizer rebuilds a prompt suited to it. The contract survives the swap. Only the implementation gets rebuilt.

A worked example

Say your signature is summarize(document) -> summary, and your metric checks whether the summary mentions every named entity in the source document.

Run the optimizer with twenty labeled documents. It might try a zero-shot instruction first, score it, then try adding two few-shot examples, score that, then try a chain-of-thought variant that lists entities before summarizing. Whichever version scores highest on entity coverage becomes the compiled prompt. You never touched the wording. You touched the metric and the examples.

Switch the underlying model from GPT-4 to Claude next month, and you rerun the same compile step against the same twenty examples and the same metric. The signature summarize(document) -> summary never changes. Only the recipe underneath does.

The full mapping

Object-oriented concept DSPy equivalent
Interface / abstract method Signature
Concrete class Module (ChainOfThought, ReAct, ProgramOfThought)
Constructor arguments InputFields
Return type OutputField
Compiler Optimizer (BootstrapFewShot, MIPRO)
Polymorphism Swapping modules under one signature

If you've spent time in Spring Boot or any typed backend, this table should feel familiar on sight. A signature is a method interface. A module is one implementation of it. The optimizer is what used to be a person doing prompt engineering by trial and error, now automated and measurable.

What next

Start with one signature for a task you already do by hand, question answering over a document, classification, extraction. Write the input and output fields with no prompt text at all. Attach dspy.Predict first, then dspy.ChainOfThought, and compare. Add ten labeled examples and a simple metric, then run BootstrapFewShot and look at what it generates. That's the fastest way to see the interface-and-implementation split in practice rather than on paper.

DSPy does need a training set and a metric to compile against, so it fits tasks with a checkable answer better than fully open-ended generation. Worth knowing going in, not a reason to avoid it.


Prasad MK writes on distributed systems, API governance, and the architecture underneath modern AI tooling.

Top comments (0)