AI Coding is rapidly evolving from code completion into an end-to-end development workflow spanning requirements analysis, solution planning, implementation, testing, and bug fixing. For Android development, this is not simply a matter of replacing an IDE autocomplete tool with a Coding Agent. It means redesigning the engineering environment in which an Agent can work reliably: it must understand the business context, respect client architecture boundaries, run builds and tests, and correct itself when something fails.
Although AI can do more and more for engineers, it will not replace them. Coding has always been only one part of software development, and engineers are more likely to evolve from people who implement code into people who use AI to build the entire development system. Rather than panic, we can keep learning and evolve alongside AI.
What Agentic Engineering Does
(Figure from Harness engineering for coding agent users)
An AI Coding system usually has three components: a foundation model, a Coding Agent tool, and the context, skills, and verification system supplied by the engineering team. All three affect the quality of the final deliverable.
The foundation model sets the upper bound for reasoning, code comprehension, and generation. As models such as Claude, GPT, and Gemini improve, their first-pass success rate on complex tasks generally rises, but so does the cost. In practice, teams trade off cost, response time, and reliability. Even with a top-tier model, complex business requirements, cross-module refactoring, and performance investigations cannot be completed reliably in a single generation pass. This is especially true in Android projects: Gradle builds, build variants, resource and manifest merging, Jetpack component lifecycles, JNI/NDK, R8 obfuscation, app startup paths, and UI rendering all increase the Agent’s comprehension burden.
Coding Agent tools organize model capabilities into an executable development loop. Tools such as Claude Code, Codex, and CodeBuddy include system prompts, file search, editing, command execution, task decomposition, and context compaction. Some tools also train models in real tool-use environments, making them better at reading repositories, changing code, running tests, and repairing failures. This surrounding system is commonly called the harness: the execution environment around the model.
The context and skills supplied by engineers determine whether the Agent acts correctly in a particular project. For an Android team, that context includes the app’s business goals, module boundaries, architecture layers, shared components, design system, build procedures, test commands, performance budgets, staged rollout rules, privacy and compliance requirements, and directories or interfaces that must not be changed casually.
(Figure Inspair by Harness engineering for coding agent users)
To enable high-quality AI delivery, engineers must do at least two things: guide the Agent to work with the right context, and enable it to verify that its output is correct. These correspond to Context Engineering and Harness Engineering, respectively.
Context Engineering asks, “What does the Agent see when making decisions?” A large language model is essentially stateless: each output is determined by the current context window. When context is accurate, complete, and concise, the Agent makes more reliable judgments. When context is disorganized, outdated, or noisy, even a strong model may generate code from faulty premises.
Harness Engineering asks, “How does the Agent know it got the job done right?” For Android, delivery cannot be judged solely by whether code was generated. It must also compile, preserve architecture constraints, pass critical test cases, conform to UI specifications, avoid performance regressions, and include the required analytics instrumentation and staged rollout strategy. Without such feedback, an Agent can mistake “I wrote a lot of code” for “the task is complete.”
From an Agent Software Factory to a Controlled Development Process
If context, tools, and verification are taken to their limits, can AI Agents do all the work of software development themselves? That is the premise of the AI Agent software factory.
(Figure from Why the Software Factory Failed)
Some in the industry have already tried a lights-off software factory: requirements enter a queue, and Agents automatically implement, test, fix, and commit changes, sometimes even handling production feedback. Such systems can look highly efficient in the short term because code generation really is faster than manual coding. But in a complex production codebase, a process where nobody reads the code or reviews the design is not sustainable.
For now, think of an Agent as a new engineer joining the team. Writing prompts, describing requirements, and assigning tasks amount to communicating requirements remotely. Even communication between people has information gaps: the business background, implicit constraints, and trade-offs in a requester’s head are hard to convey completely to the implementer, and written records inevitably contain omissions and ambiguities. The problem is more pronounced for an Agent, which can only infer the system’s intent from its current context and tool results. Research and Plan should therefore be vehicles for iterative discussion, not one-off documents. During planning, engineers need to keep asking questions, adding constraints, and correcting misunderstandings until the scope of change, architectural direction, edge cases, and verification approach are confirmed.
Does an approved Plan guarantee good code? Still no. Even code written by an experienced engineer needs review. Agents make mistakes too, whether in the form of hallucinated assumptions, incorrect API usage, duplicated logic, excessive abstraction, or inappropriate fallbacks added merely to pass tests. After multiple iterations, a seemingly minor disturbance can propagate along a dependency chain and eventually produce a tangled codebase that is difficult to understand or change. Plan Review addresses whether the direction is right; Code Review addresses whether the implementation is right. Neither can replace the other.
The underlying issue is not that the prompt needs to be better, nor can it be solved by adding a few more loops. Most current coding benchmarks reward passing tests but struggle to penalize damage to maintainability. A model can pass tests with local hacks, duplicated logic, poor abstractions, excessive fallbacks, or workarounds that bypass the type system. The costs of those choices often emerge only weeks, months, or even a year later.
No matter how much efficiency AI brings, the fundamental problems of software architecture and software engineering do not disappear. How should modules be divided? How should dependencies be controlled, state managed, concurrency coordinated, errors propagated, and systems evolved? Faster code generation does not automatically answer any of these questions. Pitfalls engineers encountered decades ago still appear today in new forms. The communication costs, system complexity, organizational coordination, and design principles discussed in classics such as The Mythical Man-Month, Peopleware, and works on distributed systems are even more worth revisiting in the AI era. AI can shorten the path to implementation, but it cannot make architectural judgments for the team or bear the codebase’s long-term maintenance costs.
Long-term maintainability is particularly important in Android codebases. A screen working today does not mean it handles lifecycle management, state restoration, resource cleanup, retries on poor networks, internationalization, accessibility, dark mode, foldables, and performance budgets correctly. A cross-platform dynamic UI approach may suit promotional pages or e-commerce campaign pages, but in a highly interactive utility app, an audio/video scenario, or an experience requiring a high frame rate, it may impose costs in time to first frame, scrolling, input latency, and native feel. Architectural trade-offs must be judged against the business model, user scale, team capabilities, and future evolution — not just immediate generation speed.
Why Plans and Code Both Need Review
(Figure Inspire by https://github.com/humanlayer/advanced-context-engineering-for-coding-agents/blob/main/ace-fca.md)
The earlier an error occurs, the greater its amplification effect. One bad line of code is a local problem; a flawed implementation plan can produce hundreds of lines of wrong code; a misunderstanding of the requirements or a faulty technical research conclusion can steer the entire feature in the wrong direction.
Human review in Agentic Engineering should therefore not be reserved for the final Code Review. The higher-leverage review points come earlier:
- During requirements analysis, confirm the user problem, success criteria, edge cases, and explicit exclusions.
- During system architecture, confirm module boundaries, data flow, API contracts, threading model, and state management.
- During program design, confirm core types, call chains, file layout, dependency direction, and error-handling strategy.
For Android development, Plan Review should at least ask: Are we changing the right module? Is the code going in the right place? Are we using the right screen architecture? Do we need to add or update unit tests? Will the change affect app startup, the main thread, or memory usage?
Where Humans Must Stay Involved
(Figure Inspire by https://github.com/humanlayer/advanced-context-engineering-for-coding-agents/blob/main/ace-fca.md)
A workflow suitable for Agents can be divided into six steps:
- Requirements or problem analysis: Input may be a feature request, bug report, production alert, user feedback, or test case. The output should clarify the business goal, reproduction steps, impact, and acceptance criteria.
- Codebase research: Have the Agent read the relevant modules, call chains, historical implementations, and test baselines, and produce a research document rather than changing code immediately.
- Solution planning: Based on the research, produce a plan that identifies which files to change, why, how to verify the changes, and where the risks lie.
- Human review of the plan: Engineers focus on whether the direction is correct, abstractions are appropriate, and verification is sufficient.
- Staged implementation: The Agent implements the plan, running a build, tests, or device verification after each runnable increment.
- Human review of code and reports: Engineers inspect key diffs, verification results, and uncovered risks, then decide whether to extend the work, roll it back, or hand it over for testing.
This process does not remove people from development. Instead, it asks engineers to shift their attention from repetitive coding to high-value judgment: clarifying requirements, making architectural trade-offs, enforcing quality gates, and accepting deliverables.
Context Engineering: Helping the Agent Work with the Right Context
Large language models do not have true long-term memory. For a Coding Agent, the current context window is the primary basis for understanding a project. Context management is not simply a matter of “feeding it more material”; it requires controlling information quality — accuracy, completeness, concision, and loading information on demand.
(Figure from https://github.com/humanlayer/advanced-context-engineering-for-coding-agents/blob/main/ace-fca.md)
A good AGENTS.md, CLAUDE.md, or project-level guide should answer the questions an Android Agent most often encounters:
- What does the app do, and what are its core user journeys?
- How are the Gradle modules organized, and in which direction do dependencies run among App, Feature, Core, Data, Design System, and Native modules?
- Which architecture is used — for example, MVVM, MVI, Clean Architecture, Redux-like state management, or a custom framework?
- What is the UI stack — View System, Jetpack Compose, or both — and how should themes, color palettes, component libraries, and design tokens be used?
- How is the data layer organized — for example, Repository, UseCase, Room, DataStore, networking SDKs, caching policies, and offline support?
- What are the build and verification entry points — for example, common variants, assemble tasks, lint, unit tests, on-device tests, screenshot tests, NDK builds, and native unit tests?
This file needs regular maintenance, but it must not grow without bound. Stuffing every module detail into the initial context consumes the model’s attention budget. Even with a large context window, too much irrelevant information diffuses attention and makes mistakes on critical decisions more likely.
A better approach is layered context: keep only a project map and navigation index at the root; record local rules in module-level documents; and put specialized knowledge about builds, tests, performance, releases, analytics, privacy, and other topics in dedicated documents. The Agent reads only the top-level guide at the start of a task, then follows the index to the relevant documents when it enters a module.
Context Engineering also requires frequent, deliberate context compaction. Code search results, build logs, failed attempts, long JSON payloads, and test output can all pollute the context. In a complex task, the Agent should compress noisy information into structured artifacts at each Research, Plan, and Implement stage: which files have been read, what it currently understands, which options were ruled out, what risks remain, and how to verify the next step. That way, the next Agent — or a human taking over — does not have to reconstruct the state from a chat history.
Rules, skills, MCP integrations, and external tools also consume context and tokens. Do not load every capability up front unless necessary. Load progressively: bring in build instructions when the task requires an Android build, and device-operation and testing skills only when UI verification is needed.
Harness Engineering: Defining What Counts as Done
Context answers, “Does the Agent know what to do?” Harness answers, “How does the Agent know it has done it correctly?”
In an Android project, harness controls fall into two categories:
- Feedforward controls: Constraints and guidance supplied before the Agent acts, such as architecture rules, module boundaries, coding standards, shared-component usage guides, and testing requirements.
- Feedback controls: Executable feedback supplied after the Agent acts, such as Gradle builds, Android Lint, Detekt/Ktlint, unit tests, Compose UI Tests, Robolectric, instrumentation tests, NDK builds, gtest, and static architecture checks.
Checks with deterministic, CPU-executable outcomes should be moved as early as possible. These include compilation, lint, type checks, unit tests, module-dependency checks, complexity checks, and coverage checks. Semantic judgments — whether a design is overengineered, tests cover the core business behavior, or a UI meets product expectations — can be supported by AI review, but engineers must still make the final call.
The point of a harness is not to dictate a rigid path to the Agent, but to define the desired deliverable clearly. Too many process constraints limit the model’s ability to discover better solutions. As model capabilities improve, yesterday’s protective procedures can become obstacles. A more robust approach is to prescribe fewer step-by-step instructions, provide more capabilities, boundaries, and acceptance criteria, let the Agent plan its own work, and catch mistakes with executable verification.
Improving Agent Output Quality
Once a Coding Agent generates code, it must first pass engineering-level verification. For Android, at a minimum, the target variant must compile, and all relevant Kotlin/Java code, resources, manifests, and native code must pass through the build pipeline. Modules involving C++ also need NDK compilation and native unit tests. UI changes need checks for shared color palettes, shared components, consistent spacing, the theme system, and accessibility properties.
But a successful build is only the lowest bar. Whether a client feature is actually correct depends on business logic, lifecycle handling, state restoration, thread switching, error handling, data consistency, and dependencies outside the client behaving as expected.
Improving Testability with IoC and DI
Unit testing is mature in the Android ecosystem, but much legacy code is difficult to test. Common problems include business logic scattered across Activities and Fragments; ViewModels that access global Managers directly; network, database, configuration, account state, and experiment flags read through singletons; utility classes that hide I/O; and Native and Kotlin layers coupled through interfaces that are difficult to replace.
Such code is not impossible to test; it is simply too expensive to test. To make a module testable, external dependencies must be separated from business logic and passed in through interfaces and constructor injection. This is the principle of Inversion of Control (IoC) and Dependency Injection (DI). On the Kotlin/Java side, teams can use Hilt, Dagger, Koin, or an in-house container. On the C++ side, dependencies can be wrapped using Fruit or a lightweight factory.
The value of dependency injection is not merely cleaner-looking code. It gives both Agents and engineers a stable way to replace dependencies. When testing a ViewModel, they can mock networking, databases, account state, experiment flags, and the time source. When testing a Repository, they can swap out remote services and local caches. When testing a Native ViewModel or business core, they can run fast local tests without an Android device.
In the era of manual coding, retrofitting DI and tests into legacy modules was often tedious. Coding Agents are well suited to this kind of well-defined refactoring: they can identify global dependencies, extract interfaces, adjust construction paths, add tests, and run verification at each step. Engineers should focus their reviews on whether interface boundaries make sense, whether testability has come at the expense of sound design, and whether excessive abstraction has been introduced.
Layering Unit Tests and Client-Side Verification
Android tests can be layered by feedback speed and fidelity.
JVM unit tests are the fastest. They are well suited to ViewModels, UseCases, Repositories, state machines, data transformations, business rules, and error handling. With dependency injection, an Agent can create or update tests for every requirements change, covering happy paths, edge cases, failures, and regressions.
Next come Robolectric and lightweight framework tests, which can validate some Android Framework-dependent logic but cannot fully replace a real device. Further down the stack are instrumentation tests, Compose UI Tests, UIAutomator, and screenshot tests, which validate screen behavior, interaction flows, and visual fidelity on real devices. Native logic should be verified quickly with local gtest runs whenever possible, rather than requiring a full app launch every time.
For screens built with Jetpack Compose, UI state should be driven by a stable state model, with Composables kept free of side effects — or limited to minimal ones — where possible. Then an Agent can build tests around state inputs and UI outputs instead of fragile tap sequences. For older View System screens, business decisions should likewise be moved out of View callbacks into a ViewModel or Presenter to make UI automation less brittle.
Observed Results
Take CreatePostViewModel.cpp, which handles editing topic posts. After a dependency injection refactor, a Coding Agent can enumerate and generate unit tests for the main business paths: drafts, topic selection, body validation, image state, publishing failures, retries, and recovery from errors. Each change runs native unit tests before moving to client integration verification. The real benefit is not "more tests"; it is catching low-level C++ logic errors before handoff to QA and giving engineers more confidence in letting the Agent change complex logic.
Preventing Feature Gaps: From Unit Tests to Integration Acceptance
Unit tests can protect client-side logic, but a requirement is never delivered simply because “the client code is done.” Complete delivery also depends on accounts, server APIs, configuration delivery, experiment flags, assets, operations consoles, analytics instrumentation, staged rollout, and test cases. Before handing a feature to product and QA, teams generally also need integration test cases to demonstrate that the primary flow works.
At its core, integration testing verifies end-to-end behavior: launch the full app, use a real or near-real account, perform a sequence of actions, and observe screen state and service responses. In other words, the system must “see what is on the device screen, perform the next action, and then judge whether the outcome matches expectations.”
The Android SDK already provides capabilities such as adb, UIAutomator, screenshots, accessibility trees, log capture, file retrieval, and Intent-based launching. Android CLI tools and community projects aimed at Coding Agents now package these capabilities into testing skills the Agent can call. Projects such as Trailblaze attempt to connect device control, screen understanding, test recording, and verification into one workflow.
One important lesson from Trailblaze is that device verification need not rely heavily on screenshots and OCR. Reading the UI hierarchy and accessibility information first can greatly reduce token usage while helping the model identify controls, text, and hierarchy more reliably. Screenshots and OCR are better used as fallbacks when the UI tree is unavailable or visual judgment is required. If verification targets Android only, the official Android CLI and Skills can also be used instead of Trailblaze for device control and integration testing.
If a QA team maintains integration test cases, a practical Android Agent verification workflow might look like this: export integration test cases from a test management platform or XMind and convert them to structured Markdown; connect a test device and install the target build; have the Agent execute each case step by step; capture the UI tree, screenshots, and logs at each step; when a failure occurs, record the current screen, action history, key logs, and suspected cause; finally, produce a test report identifying passed, unexecuted, blocked, and human-review-required cases.
Alternatively, if nobody maintains integration test cases, tests can be created with Journeys and run by the Agent using Android CLI.
To help the Agent execute those cases, provide a feature guide. It might be a user-facing wiki or a web page the Agent can access, explaining how to reach and operate the feature so the Agent does not have to guess its way through the app.
This approach can cover most stable primary flows, such as login, publishing, search, list refresh, form submission, configuration changes taking effect, and error messages. It still has limits: cross-device interactions, complex gestures, video playback experience, animation flicker, subtle visual fidelity, unstable network conditions, and perceived performance still require humans or specialized testing tools. The Agent’s value is to run the automatable integration paths first and organize clear failures and omissions, allowing engineers, QA, and product managers to focus on the problems that genuinely require judgment.
Conclusion
The diagram below summarizes the building blocks of an Android harness. Guides provide context and quality standards before coding. The Coding Agent performs Research, Plan, Implement, and Repair. Sensors provide feedback on builds, behavior, performance, and stability. The system then preserves test results, code diffs, and runtime metrics as evidence of delivery. Human Review sits at high-risk decisions and final acceptance; when tests fail or metrics regress, the feedback goes back to the Agent for correction.
The goal of Agentic Engineering is not to create an unsupervised software factory, but to help Android engineers deliver software with greater leverage. AI accelerates research, generates code, adds tests, runs verification, and compiles reports. Humans decide whether the requirements are right, the architecture is sound, the quality bar has been met, and which risks cannot be delegated to a model.
In an Android environment marked by long product lifecycles, demanding experience requirements, and fragmented devices, reliable AI Coding does not mean “getting the Agent to write more code.” It means “getting the Agent to write code with the right context and prove it is correct through executable verification.” Context Engineering determines whether the Agent understands the system; Harness Engineering determines whether it can validate the outcome; and human review determines whether the codebase remains healthy over time.
References
- Advanced Context Engineering for Coding Agents
- Why the Software Factory Failed
- Harness Engineering
- Use Feature Lists to Constrain What the Agent Does
- Awesome Harness Engineering
- Awesome Context Engineering
- Trailblaze
AI Coding for Android: Reflections and Building Self-Verification was originally published in ProAndroidDev on Medium, where people are continuing the conversation by highlighting and responding to this story.




