A few months ago, I was doing agentic coding with DeepSeek V4 Pro. It was cheap, it was fast, and it kept getting lost.
I'd watch the stream response in real time. The model would open a config file it didn't need. Then a test file. Then some unrelated utility. Then circle back. Minutes gone, nothing done. I'd sit there thinking: what a dumb model, can't do much.
Every session ended the same way. I'd give up on DeepSeek and switch to a frontier model to actually finish the thing.
I thought the problem was the model. Cheaper models have weaker reasoning, right? You get what you pay for.
The actual problem was that I wasn't giving it any direction.
What I was doing
This was my typical prompt:
Fix the login bug where users get 401 after password reset.
That's it. I was dropping the model into my codebase with nothing except the problem statement. No wonder it wandered. It had to figure out where to start, what files mattered, what "fixed" even meant. It was searching, not solving.
The change
Two things. Two lines added to every prompt.
First: success criteria. A concrete definition of done. Not "fix the auth bug" but "the login endpoint returns 200 with a valid JWT when given correct credentials." If I can't write a one-sentence check for it, the model can't either.
Second: an entry point. The exact file to start from. Not "somewhere in the auth module" but src/auth/login.ts.
The same prompt became:
Success criteria: POST /auth/login returns 200 with a valid JWT
when given the new password from the reset flow.
Entry point: src/auth/login.ts
Fix the login bug where users get 401 after password reset.
What happened
I wasn't expecting a revolution. I'd seen advice about being concrete in prompts. It sounded too simple. I figured maybe it'd save a few seconds.
The first time I used both rules together, the stream looked different. No file hunting. No detours. It opened src/auth/login.ts immediately and started working.
What surprised me more than the speed was how I felt. Before, I braced for frustration every time I used a cheaper model. Now I was just watching something do exactly what I asked. The emotional difference was bigger than the time saved.
The pattern
I started applying this to everything. Bug fixes, refactors, features. Same result: less wandering, less frustration, faster resolution. DeepSeek V4 Pro, a model I'd written off as "the cheap one," started handling tasks I used to reserve for frontier models.
The structure settled into three steps:
- Success criteria first. Define done before anything else. Make it testable.
- Entry point second. Give the file path. If the task spans multiple files, list them.
- The actual ask last. Once the model knows what done means and where to start, the task almost writes itself.
The research I found later
After this became my default, I went looking to see if there was actual research behind what I'd stumbled into. There was.
The Prompt Report (Schulhoff et al., 2024) catalogued 58 prompting techniques and found that structured prompting consistently beats vague prompts across model sizes. They call what I'm doing "output specification": telling the model what the finished state looks like, not just what to do.
Another study by Pecher et al. found that smaller specialized models needed only about 100 labelled samples to match larger general ones on text classification tasks. The variable that moved the needle wasn't model size. It was task framing.
So it's not that I discovered something new. I just found it the usual way: through frustration, by accident, after assuming the tool was broken when I was the one not steering.
What got worse
This approach has limits. If the problem is genuinely ambiguous, if I don't know what file it lives in or what "fixed" looks like, I can't write good success criteria. Forcing it anyway gives the model a wrong target, which is worse than no target.
There's also a skill cost. Writing concrete success criteria is harder than writing a vague ask. It forces me to think through what I actually want before I ask for it. That's work. But it's work I'd have to do eventually anyway, either upfront in the prompt or downstream cleaning up after a wrong turn.
For genuinely exploratory problems, or when I'm navigating an unfamiliar codebase, I still reach for a frontier model. Some tasks need broader reasoning capacity. But that's fewer problems than I thought. Most of the time, I just needed to give direction.
References
- Schulhoff, S. et al. (2024). The Prompt Report: A Systematic Survey of Prompt Engineering Techniques. arXiv:2406.06608.
- Pecher, B. et al. (2024). Comparing Specialised Small and General Large Language Models on Text Classification: 100 Labelled Samples to Achieve Break-Even Performance. EMNLP 2025. arXiv:2402.12819.
Top comments (0)