DEV Community

Cover image for Effective Agentic Development
Federico Ramirez
Federico Ramirez

Posted on AI-assisted

Effective Agentic Development

It's no secret that AI is booming right now. Both old-school senior developers and new developers alike need to learn the best way to use this new tool to get the job done.

In this post I'll share my personal experience working with agents in my projects, as well as teaching people (both experienced and inexperienced developers) how to get the most out of your agents.

The Mental Model: Model, Harness, Agent

Just so we are all on the same page, let's establish a clear mental model of how the pieces fit together. People often mix these terms up, but the distinctions matter.

The Model (The Brain)

A Large Language Model, often just called the "model." At their core, they are autocomplete machines. Given some input, they predict what comes next. There's a bunch of these models, and they are optimized for different things. In this post our main interest is using them for coding, but even within coding some LLMs are optimized for speed, others for deep reasoning, etc.

At the time of this writing, the strongest models by far are the closed ones, in particular Google's Carbon, Anthropic's Opus 5.5, and OpenAI's GPT-6.1 Astra.

That being said, most coding tasks don't really need all that power. Open models such as DeepSeek 4.1 flash and GLM 5.3 are also very popular choices, especially because they can be much cheaper than the closed options.

The most popular way to compare models is using benchmarks, with Artificial Analysis being one of the best resources.

The Harness (The Body & Hands)

A harness is the application that manages an LLM. It gives the model "hands" by allowing it to call tools (such as reading or editing files, running terminal commands, or controlling a browser), manages context windows, and drives the main chat loop you see. Some popular ones are Claude Code, Codex, Cursor, and Antigravity, alongside open-source options like Pi and OpenCode designed to work with any model.

Most AI providers with their own models will also offer their own harness, optimized for their models, and they might support third-party models via API keys (known as BYOK, bring-your-own-key). Personally, if I'm using a company's model, I'll try to use that company's harness because everything just works smoother that way. Also, most of the time they subsidize tokens inside their own harness versus the raw API key approach, meaning you can pay several times less for the same amount of tokens.

A harness can run in the terminal, or it can be a desktop app. They offer very similar features, and it's up to you to pick whatever you feel most comfortable with. Most people prefer the desktop UI, which makes sense. In my case, because I've been using the terminal for coding since forever, it feels much more efficient to use the terminal version. It's just a matter of preference.

The Agent (The System)

An agent is a self-directed system capable of executing complex workflows by pairing an LLM (the "brain") with a harness (the "body").

While a raw AI model is passive, responding only when prompted and stopping immediately after generating text, an agent operates within a continuous evaluation loop. It receives a specific goal, reasons through the steps, uses tools in your workspace to execute code, inspects the outcome of its actions, corrects its own errors, and adapts its strategy autonomously until the task is successfully completed.

Alignment: Decide Before You Code

Just writing a quick prompt is rarely enough for an agent to build what you actually want. Most of the time you don't even realize how many decisions you left unmade.

You might ask an agent to "create a feedback form", but you haven't made a decision on what color the submit and cancel buttons should be, or if it should even have a cancel button at all, or what layout the form will use. Should it be an inline form, or does each input get its own line?

Many modern harnesses offer a dedicated "plan mode." In practice, with modern models, plan mode is mostly a guardrail that lets you talk to the AI and read files without letting it write or edit code. But the planning itself can be done just by prompting. You don't need a special mode to plan.

The problem is, for complex features, casual back-and-forth is rarely enough. It leaves too many decisions unsettled, giving the AI room to guess and improvise during implementation. That is where rework comes from.

That's why I really like the grilling skill by Matt Pocock. Grilling is a strict planning loop that relentlessly asks you questions until there are no more open questions. After each round of answers, it re-computes its understanding and asks again if needed. It's a simple skill, but a brilliant idea.

The result of grilling is essentially a concrete specification. This ties directly into Spec-Driven Development: settle all the decisions, edge cases, and constraints into a clear spec first, and then hand that spec to the implementer. When the agent has a solid spec, it can one-shot the work cleanly without guessing.

Skip grilling for simple tasks, or when you actually want the AI to make design decisions for you. But for big and complex features where you want control over architecture and UX, it's great!

Context Hygiene & Execution Architecture

The context window (most of the time simply called context) is the maximum amount of text and data, measured in tokens (units of words or subwords, roughly 1 token ≈ 0.75 words), that the model can process, read, and "remember" during a single interaction.

Most modern models support up to 1 million tokens or more. That means they support long coding sessions where the model can read lots of files and keep a long history without "forgetting" things.

That being said, the bigger the context gets, the more chances the model gets confused. This is known as Context rot:

Context rot is the progressive decline in a large language model's (LLM) accuracy, recall, and reasoning quality as its input context grows longer.

Some models handle it better than others, and modern models do a good job preventing it for the most part, but you should still be mindful of context, not only because of context rot, but because stale context can easily steer the model the wrong way.

One Job = One Session

Actually, one of the most common mistakes I see people make is just reusing the same session for everything.

Treat sessions as disposable: One job = One session.

When context gets too big, harnesses will compact it. Compaction is basically creating a summary of the context so the agent can continue to work. The problem is, no matter how good compaction is, it's always lossy. Every compaction drops details, and with every pass you risk degradation.

Ideally, your sessions should finish the job sooner, and then for a new job, you just start a clean session. The only exception is when tasks are tightly related and benefit from the same exploration. Even then, when the context gets big (65%+), consider using an intentional handoff skill to capture decisions and start fresh rather than dragging stale context along.

Implementing with Subagents

A great way to save up on context is using another agent to do the implementation work.

Let's say you start up a new session with your default model. You talk to the agent and together you brainstorm a new feature. At this point maybe you've used 10% to 15% of your context window, because the agent had to search files and do research.

If you implement directly using that same agent, it will read files, edit files, write tests, run them, and just reason until the work is done. That's perfectly fine, but after that task, your context will easily grow to 30%, 50%, or more. If you need to iterate further, the context builds up fast with every change, and you risk context rot.

What you can do instead is have your main agent act as an Orchestrator that coordinates dedicated subagents.

Think of the orchestrator as a project lead: its job isn't to grind through every file edit, terminal log, or test failure itself. Its job is to hold the big-picture plan, break work down, and dispatch worker subagents with clean contexts to tackle specific tasks.

              ┌─────────────────────────────────────────┐
              │                   You                   │
              └────────────────────┬────────────────────┘
                                   │
                                   ▼
              ┌─────────────────────────────────────────┐
              │       Orchestrator (Main Session)       │
              │  • Holds the plan & big-picture goals   │
              │  • Context stays lean & responsive      │
              └───────────────┬─────────────────────────┘
                              │
               Dispatch tasks │ Receive concise
               with clean ctx │ summaries back
                              │
              ┌───────────────┴─────────────────────────┐
              ▼                                         ▼
┌───────────────────────────┐             ┌───────────────────────────┐
│   Subagent: Implementer   │             │    Subagent: Reviewer     │
│ • Explores codebase       │             │ • Fresh, unbiased eyes    │
│ • Edits files & runs tests│             │ • Inspects the final diff │
│ • Context discarded after │             │ • Context discarded after │
└───────────────────────────┘             └───────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Each subagent dives in, does the heavy lifting in an isolated context, and reports back a concise summary. Because all the messy file reads, compiler errors, and tool outputs stay contained inside the subagent's disposable window, your main orchestrator session stays lean, responsive, and rot-free.

Just telling your agent something like:

Implement this feature in a subagent

is often all it takes!

Choosing the Right Model for the Role

It's hard to give rigid advice on what model you should choose for a particular problem. At the end of the day it does take some experience and knowledge of the actual work.

You can also let the AI decide for you, it will get it right most of the time :)

This is the general rule of thumb:

  • Orchestrator: A big, smart model that can delegate to subagents and track the overall plan.
  • Brainstorming & Planning: A deep reasoning model that can look into all the details and spot edge cases.
  • Implementer: Sized to the task. If it needs to make tricky architectural decisions, use a big model. If the work is very mechanical or well-specified, a smaller, faster model (like DeepSeek or a Flash tier) works great.
  • Reviewer: At least equal to or smarter than the implementer (more on this in a second).

Verification: Close the Loop

"Human in the Loop" is a common term for when the AI needs to wait for a human to continue. Every time you catch yourself doing this workflow:

  1. AI does some work
  2. Human manually checks or runs something
  3. AI resumes work based on human feedback

it means you can optimize and take the human out of the loop.

Here are three real-world examples of closing the loop:

  1. Runtime app verification. The trickiest version of this is when the agent edits a few frontend files and tells you it's done without ever booting the server. You open the browser, click the submit button, see an uncaught TypeError: Cannot read properties of undefined in the DevTools console, copy the error, and paste it back into chat. You just became your agent's manual QA tester.

    Instead, give the agent a browser tool (via a browser MCP or Playwright) or curl. It boots the local dev server, navigates to the page, clicks inputs, checks console logs and network calls, catches runtime crashes itself, and verifies the feature works before pinging you.

  2. Database migrations and test data. An agent adds a database model or column. It writes the migration file, stops, and asks: "Please run npx prisma migrate dev in your terminal." Or worse, it writes queries against empty tables, gets an empty array back, and assumes the API works because nothing threw an error.

    Instead, let the agent run migrations against a local database and seed test records. It applies the schema change, populates realistic rows, hits the endpoint with curl, and confirms the database roundtrip works end-to-end.

  3. Remote CI checks. The agent opens a pull request and marks the task complete. Five minutes later, GitHub Actions turns red. You open GitHub, scroll through raw workflow logs, copy the stack trace, and paste it back into chat: "CI failed on step X, fix it." You just became a human copy-paste bridge between two machines.

    Instead, give the agent the GitHub CLI (gh). It watches its own PR run with gh run watch, pulls the failure log directly with gh run view --log-failed, pushes a fix, and only notifies you once CI is green.

In the age of AI, people are complaining about unit tests, saying they are useless. In my experience, those people never understood what tests are for. To make sure your application is actually doing what it's supposed to be doing, you need proper verification. Unit tests, integration tests, and live browser automation aren't chores. They are the guardrails that give the AI a way to self-correct without needing a human to babysit it.

Independent Code Review: Clean Context Matters

Implementing with subagents is a great optimization for cost, time, and context. For code review, though, I would not say it's optional: it's essential.

The thing is, after your agent does the work, it will have a strong notion of what changed and how things should work. If you ask that same agent to "do a code review", it will suffer from confirmation bias: it will "remember" the changes it intended to make instead of critically looking at what's actually in the diff. It will routinely overlook its own blind spots.

To get an effective review:

  1. Always use a fresh, clean context. The reviewer should see only the diff, the project rules, and the requirements, completely unbiased by the back-and-forth implementation history.
  2. Use an equal or smarter model. If you used Sonnet 5.5 for implementation, review with Opus 5.5. The same model will tend to overlook the same things in coding and review; a smarter model with a clean context will catch regressions and architectural slip-ups the authoring agent completely missed.

While I arrived at this workflow through trial and error across my own projects, it's reassuring to see industry research converge on the exact same conclusion. Google created an open-source methodology for specification-driven development around this called the elephant-goldfish model: the "elephant" holds the long-horizon project memory and architectural intent to draft specifications without writing code, while disposable, zero-context "goldfish" agents review diffs and test implementations without inheriting the historical baggage of the main session. Managing context with subagents is simply putting that methodology into practice.

Skills & Workflows in Practice

To make these patterns second nature, I use and build custom skills.

A skill is usually just a Markdown file in a folder on your machine that the agent reads on demand. More advanced skills might include a helper script or reference docs, but the core is plain text: instructions that teach the agent how to execute a specific workflow.

The way I use my skills, I like to define small, single-purpose skills and compose them into more complex workflows:

  • Single-purpose: Do one thing, and do it well (e.g. implement, correctness-review).
  • Workflow: Compose multiple smaller skills into end-to-end automations (e.g. address-issue).

Here are a few skills I use in my day-to-day work:

  • Implement: Breaks a scope into TDD tasks, selects the model size automatically if available, and implements with subagent review after each step.
  • Review Loop: Runs two distinct review passes (one for correctness, another for refactoring) and fixes blockers and should-fix items in a loop.
  • Grilling: Uncovers all open questions and locks down decisions before implementation starts.
  • Handoff: Creates a structured Markdown summary of decisions and context so you can transition into a clean session without lossy compaction.

You can find my personal skills on GitHub.

An End-to-End Workflow in Action

Let's say you want to create a new page for submitting feedback. Here is what a complete, closed-loop workflow looks like in practice:

Step 1: Alignment

/grilling add a new form so a logged in user can submit feedback
Enter fullscreen mode Exit fullscreen mode

Grilling asks targeted questions about validations, auth states, button behaviors, and edge cases until all decisions are locked into a clear summary.

Step 2: Implementation & Verification

/implement
Enter fullscreen mode Exit fullscreen mode

Because the context already contains the agreed-upon specification from grilling, the agent immediately gets to work. We don't need to give it more prompt context. It breaks down the problem, writes tests, implements the code, and boots the app to verify functionality using available browser tools or test suites.

Step 3: Clean Review Loop

/review-loop
Enter fullscreen mode Exit fullscreen mode

This launches fresh review passes in an isolated context to evaluate the diff for bugs and code quality, automatically resolving issues until no blockers remain.

Step 4: Ship

Open a PR
Enter fullscreen mode Exit fullscreen mode

This opens a pull request with a clear description and verification summary, ready for final human review.

Running each step manually helps when you only want a slice of the workflow. You might want to run /grilling to think through an idea without writing code yet, or invoke /review-loop on changes you wrote yourself.

Once you trust the individual steps, having a human trigger each command one by one becomes another unnecessary bottleneck. That is where workflow skills come in. My address-issue skill composes all of these primitives into a single command. It takes an issue, runs it through grilling, delegates to implementers, verifies the running app, runs the review loop, and opens a formatted pull request without needing you to babysit the transitions.

Conclusion

Agentic coding is a shift in how we work with software tools. If you treat an agent like an autocomplete box in an endless chat window, you will run into context rot, hallucinated APIs, and frustrating rework.

The difference between a frustrating agent session and an autonomous one comes down to a few disciplined habits:

  1. Decide upfront. Use strict alignment like grilling to turn fuzzy requests into solid specs before code is written.
  2. Protect your context. Treat sessions as disposable. One job per session, and delegate heavy file operations to clean-context subagents.
  3. Close the verification loop. Give the agent tools to run your tests, start the dev server, click the UI, and check CI so it catches its own mistakes.
  4. Review with fresh eyes. Never let an agent review its own code in the same session. Always review with a clean context and an equal or smarter model.
  5. Automate the pipeline with skills. Start with small, focused playbooks, then compose them into hands-off workflows.

Start small. Try writing a single skill for a task you repeat every day, or enforce the one-job-per-session rule on your next ticket. Once you build the guardrails, you can step back and let your agents do the heavy lifting.

Top comments (0)