adjoe Engineers’ Blog
 /  Frontend  /  AI Agents for Frontend Development
Frontend

Lessons from Shipping 50+ Frontend Tickets with AI Agents

At adjoe, staying aligned with the latest developments in software engineering is integral to how we build software. With “Move Fast” being one of adjoe’s core values, adopting AI Agents is part of the development workflow.  

AI Agents provide a significant productivity boost, enabling teams to deliver software more efficiently across domains such as backend, frontend, and data science. As a frontend developer, I’ve been using AI Agents to write code, generate unit and integration tests, and assist with code reviews.  

In the past few months, I’ve shipped more than 50 production tickets with AI Agents, from minor bug fixes to features spanning over 10K lines of code. Overall, my experience has been largely positive.

In this article, I’ll be discussing practical lessons learned from frontend development with AI Agents that you can apply to your projects. 

Improve AI Coding Agents with Structured Frameworks

Working with AI Agents without defined rules often leads to inconsistent results. Try spinning up a new session and ask the agent to implement a design for a new feature that uses existing components in your codebase.  

Unless specifically instructed, the agent may recreate a component that existed in your codebase, resulting in duplicate code. A developer guiding the coding session would need to instruct the agent to use the existing shared component instead. This manual intervention adds iterations and increases token usage. 

What if the agent already knows that such a component exists, or at least knows to look first in the shared library for such a component? This would streamline the process and help the agent achieve the result with fewer iterations.  

This can be achieved using pre-defined configuration files, for example AGENTS.md files, which the agent loads into the context before writing the code, resulting in better results.   

AGENTS.md files help agents understand the project structure and follow recommended practices while using less context. However, they do not provide a complete development framework. 

For consistent results, a broader set of rules, or “Skills,” can be defined.
One framework I have used successfully is Superpowers. It defines a collection of skills as Markdown files that guide the agent through different stages of the implementation process.  

Some example Skills: 

  • brainstorming: Loaded by the agent at the start with the purpose of understanding the requirements and asking clarifying questions. The main action is crafting the design spec that lists all the acceptance criteria, then receiving user review before implementation. 

  • writing-plans: Makes use of the design spec to write a detailed implementation plan which the user can review for any changes, then proceed to execution.

  • test-driven-development: pairs with writing-plans so that tests are first generated for the feature or fix required. Then the implementation is tested against pre-created test cases.

  • subagent-driven-development: executes the plan one task at a time by dispatching a sub-agent for each task. It minimizes sub-agent context.  

Using Opencode as an Agent, I used Claude Sonnet 4.6 to implement a new section in a form. The new section contains a toggle button with three options and two custom inputs. I ran the agent twice, under the following conditions:

  1. Using superpowers alongside multiple AGENTS.md files
  2. Without superpowers and with no AGENTS.md files

To meet the acceptance criteria, I had to prompt the model several times. A summary of both sessions can be found below. 

Aspect With Superpowers/Instruction filesWithout Superpowers/Instruction files
DurationApproximately 2 hours for the first working version satisfying all acceptance criteria.

It involves human interference time for testing and verification.

Same duration.
WorkflowThe agent first collects the required information, asks clarifying questions, and proceeds with the implementation.The agent jumped directly to the implementation without clarifications.

It made some decisions that didn’t align with the requirement but were missing from the ticket description.
TestingThe agent followed a TDD approach, writing tests first using the agreed-upon spec.

Then writing the code to make the tests pass.
The agent didn’t write tests unless prompted. 

With the non-deterministic nature of LLMs, there’s no actual guarantee that the agent output adheres fully to the instructions provided. However, the use of AI Agents within a well-defined framework and project-specific rules and guidelines can help achieve more consistent results. 

Quality Code vs. Fast Shipping: The Trade-offs  

Writing code is no longer challenging. AI Agents write code at phenomenal speeds, and the amount of code generated can get so overwhelming that the main burden for development teams is now the code review.

To address this challenge, there are two philosophies:

  • Allow agentic systems to work with minimum human oversight:
    This is a result-oriented philosophy. By performing minimal code review and focusing on the result, teams can move at much greater speeds. In the end, if the code satisfies the requirements – with these requirements well-defined with proper test suites – then it should be good enough for production.

  • Review every line of code written by agents:
    This is a detail-oriented approach. With this approach, all code reaching production is fully owned by the development team. Speed gains from using Agents in this case are not much, but quality and consistency should be much higher.

While the first approach sounds tempting, following it can result in great technical debt, which can make adding new features time-consuming. Having a human in the loop ensures that code is aligned with the standards and best practices enforced within the team.

A more real-world scenario is choosing the best of both worlds. While the speed gains from using autonomous agents are really great, sacrificing some of that speed to guarantee code ownership and conformity with team standards sounds more reasonable. 

The choice to go for fast shipping at the expense of validating all AI-generated code is subjective. It depends on the needs of each team and the criticality of the application. 

The Right AI Model for Each Task 

While using a frontier model for every task can be tempting, not every task needs such power. With a well-defined AI development framework, less capable models can deliver the required results for many tasks. Simple changes like adding a new dialog or expanding a form can be handled effectively with these lighter models using clear guidelines.

The cost of running powerful models is also extremely high, and doing so for all types of tasks will certainly impact the budget for AI usage. To illustrate this point, I used three models covering the whole spectrum of model capabilities to fix a minor UI bug:

GPT‑5.6 LunaClaude Sonnet 4.6GPT-5.6 Sol
No. of iterations555
Cost (USD)0.651.532.80
ImplementationStraighforwardStraighforwardOver-engineered the solution. Modified a base component in a shared library.

For all 3 models, I supplied a basic prompt in the beginning. It directed the agent to use Jira and Figma MCPs to fetch the bug description and implement the solution:

Pull the ticket description from JIRA and implement a fix for the bug described. The UI should look like this design:
<--- Figma MCP Link Here --->

The number of iterations mentioned in the table is the number of prompts I had to type for the agent to achieve the correct result as per the design.

As can be seen in the above table, and if we use GPT-5.6 Luna as the baseline for cost comparison, Claude Sonnet 4.6 was 1.35x more expensive, and GPT-5.6 Sol was 3.3x more expensive. 

In Opencode, you can set up different agents to use different models. For example, token spend can be optimized by pinning a low-cost model to the “Explore” subagent, while still using a more powerful model for the primary “Build” agent:

// opencode.json
  {
    // other configs
    "agent": {
      "build": {
        "mode": "primary",
        "model": "claude-opus-5"
      },
    "explore": {
      "mode": "subagent",
      "model": "gpt-5.6-terra"
    }
  },
}

The takeaway here is that powerful models should be reserved for complex tasks, and for everyday simple tasks, cheaper models are totally fine. Some examples of frontend tasks that would benefit from using a powerful model:

  • New feature designs involving new components across different modules,
  • Refactoring tasks that include new architecture decisions,
  • Debugging performance and UI responsiveness issues.

For everyday tasks like:

  • Extending an existing form,
  • Building new UI from previously built components,
  • Displaying a dialog with actions,
  • Or writing unit tests,

basic models would be sufficient and very cost-effective.

Context Windows and Cost Impact

Spin up a new coding session, and just prompt the agent saying “Hi”. Chances are the context gets filled with 30-50K tokens. Add to that all the AGENTS.md files and the skill files (from your framework of choice), you’ll see how the context gets inflated with every prompt.  

Bigger context leads to Attention Dilution, where the model loses track of important information. Another issue that arises with bigger contexts is Context Poisoning, where previously loaded errors and incorrect implementations can affect the agent’s output negatively.   

As the session grows, the API requests the agent makes to the model carry all context accumulated throughout the session. From a cost perspective, bigger context windows lead to significant expenses. 

Context Engineering comes to help here, with the main purpose of tailoring the session context to the task being performed. 

This can be achieved in multiple ways:  

  • Sub-agent driven approach: a main agent spins up sub-agents to perform scoped tasks. This keeps the context window from getting unnecessarily big.  

  • Compacting sessions: sessions get summarized and compacted at defined checkpoints. After preparing the design spec, crafting the implementation plan, and before code review, etc. It helps manage the context window for long-running sessions. 

  • Toolcall filtering/truncation: a tool like rtk can filter command outputs before they reach the agent context. 

  • Language Server Protocols: by directly querying the LSP, an agent can get a function signature and find all its references instead of scanning the whole codebase. This saves a significant amount of tokens. 

Managing a coding agent’s context window is more important than ever, even with the availability of larger context sizes. From my experience, when sessions grow beyond 300-400K tokens, model output quality begins to degrade, making session compaction necessary.  

Frontend with AI Agents: Key Takeaways

AI Agents for frontend development are most valuable when treated as collaborators rather than simple code generators.

The quality of the outcome depends on how effectively developers define goals, provide context, and validate decisions. Optimizing agentic workflows depends on how well the tools are integrated into the way developers work. 

Using AI Agents effectively is also about managing costs. Choosing the right model for each task and keeping workflows focused can reduce expenses while maintaining quality.

At adjoe, we’re constantly exploring new ways to build the latest ad technologies. If you’re excited about frontend development, AI Agents, and solving complex engineering challenges at scale, check out adjoe careers.  

Want to read more engineering stories? Explore the engineer’s blog for insights from the team.  

Build products that move markets