A project fails to compile, but the solution build keeps going. You already have an error to fix, while other projects continue using build time to tell you the solution still doesn’t build.
Visual Studio 2026 version 18.11 adds an opt-in way to stop a solution build after the first project failure. If your next step is to fix that failure and try again, you can make the build stop there too.
Choose when the build should stop
Open Tools > Options > Projects and Solutions > Build and Run and enable Stop solution build after first failure.
When a project fails, Visual Studio stops starting queued projects and safely stops projects already building. It still reports the build as failed, not canceled. You don’t have to watch for the first error and manually cancel the remaining work.
This stops the solution build, not only the projects that depend on the failed project. That’s an important choice: you get back to the first problem sooner, but you don’t collect the results of every remaining project in that build.
If you prefer to see those results before you start fixing errors, leave the setting disabled. That preserves the existing behavior. You can also turn it off again on the same settings page.
Measure the wait after a failure
In four test builds, post-failure completion time averaged 26 seconds with the setting disabled and 1.5 seconds with it enabled. That’s about a 94% reduction in the wait after a failure, not a claim that successful builds run 94% faster.
Your results depend on the solution and where a failure occurs. A solution with a long dependency chain or substantial parallel work can have more remaining work to stop than a small solution that finishes almost immediately anyway.
The Developer Community request describes a concrete version of this problem: a packaging project kept trying to build even after a dependency failed. The new option gives you a solution-wide stopping policy rather than requiring you to interrupt the build yourself.
Keep the two decisions separate
This setting answers when should a failed build stop? It doesn’t decide which projects you build in the first place. Existing Build and Run options cover choices such as building only startup projects and dependencies when you run or debug.
Try stop-on-failure on a solution where you normally cancel the build after seeing an error. Compare that post-failure wait, then keep the policy that fits how you troubleshoot.
A 2025 empirical study analyzing more than 500,000 Android APKs found that 92.5% of the top 1,000 Google Play apps in its 2023 dataset used some form of code obfuscation. Among apps ranked outside the top 30,000, that figure was 61.9%.
The findings suggest that widely used applications are more likely to include obfuscation and multiple obfuscation techniques. However, the researchers also caution that developers should not treat obfuscation as a complete security solution.
Android applications are often susceptible to reverse engineering because APKs contain compiled code and resources that attackers can inspect with readily available tools. Decompilers can transform DEX bytecode into a readable approximation of Java or Kotlin source code, though the output is not identical to the original.
Without protection, attackers may be able to inspect proprietary algorithms, licensing logic, application workflows, endpoint information, and security controls. They may also modify the package, bypass client-side restrictions, or repackage the application.
Android app obfuscation makes this analysis more difficult. A layered approach combines basic identifier renaming with control-flow transformations, selected string and resource encryption, and runtime checks.
This article explains why baseline renaming is not enough and how layered application protection addresses both static and runtime analysis.
How attackers reverse-engineer unprotected Android apps
An Android APK is a ZIP-based package that can contain DEX bytecode, resources, a manifest, certificates, and native libraries.
Attackers can use decompilers, disassemblers, and package-analysis tools to examine these components. Depending on how the application was built and protected, they may be able to:
Generate a readable approximation of Java or Kotlin source code
Inspect application resources and configuration
Locate API endpoints and feature logic
Analyze authentication and licensing workflows
Copy proprietary algorithms
Patch client-side conditions
Add or modify application behavior
Repackage and redistribute the application
Obfuscation does not make these activities impossible. Its purpose is to increase the time, expertise, and effort required to understand or modify the application.
Applications that process financial information, health data, proprietary logic, credentials, licensing controls, or premium features may face greater consequences if an attacker successfully analyzes or modifies them. However, any distributed application can be inspected once it reaches a user-controlled device.
Developers should also avoid treating obfuscation as a way to secure permanent API keys or privileged credentials. Secrets embedded in a distributed application should be considered recoverable. Keep privileged credentials and sensitive authorization decisions on a trusted server.
A layered mobile app security strategy combines application hardening with secure coding, server-side authorization, dependency management, signing, testing, and platform security controls.
Why R8 and ProGuard-compatible rules are only a baseline
Android teams often assume that enabling minification in a release build provides all the protection an application needs.
R8 is the optimizer included with the Android build system. Depending on the Android Gradle Plugin version and build configuration, teams enable it through the release build’s optimization or minification settings. R8 uses ProGuard-compatible configuration rules, but R8 and ProGuard are not the same tool.
R8 performs several valuable operations:
Code shrinking to remove unreachable code
Resource optimization and shrinking in supported configurations
Logical optimizations such as method inlining and class merging
Identifier renaming, also known as minification or obfuscation
These capabilities can reduce application size, improve performance, and make decompiled code less readable. R8 is therefore an important part of a release build.
However, R8 is not intended to prevent every form of static or dynamic analysis. Identifier renaming may replace meaningful class, field, and method names with shorter identifiers, but the application’s behavior must remain executable. Attackers may continue to analyze control flow, data usage, resources, strings, and runtime behavior.
Dynamic instrumentation tools create another challenge. Instead of relying solely on decompiled output, an attacker may observe a running application, hook methods, intercept calls, alter values, or inspect in-memory data. Identifier renaming alone does not address these runtime techniques.
A layered Android protection strategy may therefore include:
R8 renaming, shrinking, and optimization
Control-flow obfuscation to make decompiled logic harder to follow
String encryption to make selected strings harder to locate through static searches
Resource encryption for selected assets and raw resources
Runtime checks for debugging, hooking, tampering, rooted devices, and emulators
Each layer addresses a different analysis or attack technique. No individual layer guarantees that an application cannot be reverse-engineered or modified.
Layered obfuscation: Renaming, control flow, encryption, and runtime defense
A layered approach combines multiple protections rather than relying on identifier renaming alone.
Make decompiled output harder to interpret with renaming and control flow
Identifier renaming replaces meaningful class, method, and field names with shorter or less descriptive names. This removes some of the context that helps an attacker understand what the code does.
In the recommended DashO Android Gradle integration, R8 handles renaming, removal, and optimization. DashO Android Mode works alongside R8 and adds protections that R8 does not provide, including control-flow obfuscation, selected string and resource encryption, and runtime checks.
Control-flow obfuscation changes the structure of compiled methods while preserving their intended behavior. It can make decompiled logic more difficult to follow and reduce the usefulness of automatically reconstructed source code.
DashO protection features include configurable control-flow transformations for compiled Java, Kotlin, and Android code. These transformations are intended to increase the effort required to understand application logic.
Control-flow obfuscation does not make analysis impossible. Teams should treat it as a single layer that delays and complicates reverse engineering, rather than as a guarantee that no attacker can reconstruct the application’s behavior.
Protection strength and compatibility can vary by application, platform, DashO version, and configuration. Teams should test protected builds carefully, especially when applications use reflection, serialization, generated code, dependency injection, or other dynamic behavior.
Use string and resource encryption selectively
Attackers frequently search compiled applications for readable strings that reveal useful context. These strings may include:
API and service URLs
File paths
Error messages
Feature names
Licensing messages
Query fragments
Internal commands
Configuration values
Readable strings can help an attacker locate relevant code paths even when identifiers have been renamed.
String encryption transforms selected strings in compiled code and injects the logic required to decrypt them at runtime. This makes the protected values more difficult to locate through a basic static search.
DashO applies String Encryption broadly by default, but teams can use include and exclude rules to target specific methods, classes, or packages. Teams should determine where string encryption provides meaningful protection value and test the resulting application. String encryption can interact with reflection and other features that rely on class names stored as strings, so appropriate R8 keep rules or DashO exclusions may be required.
String encryption also does not make an embedded credential permanently secure. If an application can decrypt and use a value, an attacker controlling the device may eventually be able to observe it. Permanent secrets should remain on a trusted server.
DashO also supports encryption for selected Android assets and raw resources through Android Mode and the DashO Gradle Plugin for Android. The application decrypts protected resources when they are accessed at runtime.
Resource Encryption is configurable and disabled by default. Because it may affect resource access and runtime performance, teams should run functional and performance tests after enabling it, particularly on lower-powered supported devices.
A thorough application security risk assessment can help teams determine which strings, resources, and code paths warrant additional protection.
Detect tampering, debugging, and unsafe runtime environments
Static obfuscation makes compiled code more difficult to understand, but it does not detect activity that occurs while the application is running.
Runtime checks can identify configured conditions associated with dynamic analysis or unsafe environments. DashO supports checks for conditions, including:
Debugging
Hooking
Tampering
Rooted Android devices
Android emulators
Application expiration
When a check detects a configured condition, the application can respond according to the selected settings. Responses may include notifying application logic, terminating execution, or triggering another configured action.
Runtime checks do not automatically guarantee that every debugger, hook, emulator, rooted device, or modification will be detected. Attackers may attempt to bypass or turn off the checks themselves. For this reason, checks should be distributed thoughtfully and combined with static protections.
Teams should also consider the user impact of defensive responses. For example, legitimate users may run applications on rooted devices or approved emulators. An immediate application exit may not be appropriate for every product or risk level.
Test both the normal and detected conditions before release. Runtime checks may introduce overhead or unexpected behavior depending on their number, placement, frequency, and response configuration.
Together, static obfuscation and runtime checks provide stronger protection than identifier renaming alone. They should still complement—not replace—secure architecture, authentication, authorization, dependency scanning, signing, and security testing.
How DashO works with R8 to protect Android applications
DashO is an application-hardening and obfuscation tool for Android, Java, and Kotlin applications.
For current Android Gradle projects, the recommended integration is to use DashO Android Mode alongside R8. The responsibilities are divided between the two tools:
Protection
R8
DashO Android Mode
Identifier renaming
Yes
Handled by R8
Code removal
Yes
Handled by R8
Build optimization
Yes
Handled by R8
Control-flow obfuscation
No
Yes
Selected string encryption
No
Yes
Selected Android resource encryption
No
Yes
Runtime checks
No
Yes
This division is important. DashO does not replace R8 in the recommended Android Gradle integration. R8 should remain enabled to handle renaming, removal, and optimization, while DashO provides additional layers of protection.
DashO processes compiled code during the build rather than modifying the application’s source files. This allows developers to continue working with readable Java or Kotlin source while generating protected release artifacts.
The DashO Gradle Plugin for Android integrates protection into the Android build process. It manages the inputs and outputs passed to DashO and can apply protection automatically to selected build variants.
DashO can also post-process APKs and Android App Bundles in supported configurations, although PreEmptive recommends integrating DashO directly into the Gradle project when possible. Build integration provides greater control and makes it easier to apply protection consistently.
PreEmptive was founded in 1996 and currently reports more than 5,000 corporate clients across 101 countries. Teams should still evaluate DashO using their own code, supported Android versions, build configuration, performance requirements, and threat model.
Feature availability and behavior may vary by DashO version, integration mode, application type, and configuration. Review the current product documentation before selecting protections for a production build.
How to add Android app obfuscation to your build
The recommended Android workflow integrates DashO with Gradle and keeps R8 enabled.
1. Enable R8 for the release build
Confirm that R8 optimization or minification is enabled for the release variant. R8 will handle renaming, removal, and optimization when used with DashO Android Mode.
Review existing ProGuard-compatible rules and remove overly broad keep rules where possible. Broad rules can prevent R8 from renaming, shrinking, or optimizing significant portions of the application.
2. Add the DashO Gradle Plugin for Android
Use the DashO project wizard or follow PreEmptive’s current integration instructions to add the plugin to the Android project.
The wizard updates the Gradle configuration and creates a DashO project file. It does not overwrite the application’s source files.
Confirm that the versions of DashO, the DashO Gradle Plugin for Android, the Android Gradle Plugin, Gradle, Java, and Kotlin are compatible.
3. Start with the default protected release build
Generate a protected release build using the default Android Mode configuration. Control Flow and String Encryption are enabled by default with relatively conservative settings, while Resource Encryption is disabled.
Test this initial build before changing the protection settings. This establishes a working baseline and helps teams determine whether an issue comes from the integration or a later configuration change.
4. Configure protection layers incrementally
Based on the application’s risks, review, tune, or enable protections such as:
Stronger control-flow settings
String Encryption include and exclude rules
Selected resource encryption
Debug checks
Hook checks
Tamper checks
Root checks
Emulator checks
Shelf Life checks for time-limited builds
Not every application needs every protection. Select and configure controls according to the application’s threat model and user requirements, testing after each meaningful change.
5. Configure keep rules and exclusions
Applications that use reflection, generated code, serialization, dependency injection, or string-based class loading may need additional R8 keep rules or DashO exclusions.
Avoid excluding more code than necessary. Excessively broad exclusions reduce the amount of code that can be protected.
6. Test the final protected application
Run functional, regression, performance, and security tests against the protected release artifact.
Testing should cover:
Startup and common workflows
Reflection and dependency injection
Authentication and authorization
Resource loading
Application performance
Crash reporting and protected stack traces
Tamper, debug, hook, root, and emulator responses
Upgrades and migrations
Supported Android versions and devices
Test both normal execution and the configured detection conditions.
7. Automate protection in CI/CD
Run DashO and R8 as part of the controlled release pipeline so that every production artifact receives the approved protection configuration.
Retain the DashO configuration, R8 rules, mapping information, build logs, and other required release artifacts. Restrict access to files that could make protected builds easier to analyze.
Confirm that the artifact tested by the team is the same artifact signed and published to users.
Android app obfuscation checklist
Build configuration
Enable R8 for the release build.
Review ProGuard-compatible rules.
Remove unnecessarily broad keep rules.
Confirm DashO and Android build-tool compatibility.
Apply protection only to intended build variants.
Static protection
Enable appropriate control-flow obfuscation.
Encrypt selected sensitive strings.
Encrypt selected assets or raw resources where appropriate.
Keep permanent secrets out of the application.
Configure narrow exclusions for dynamic code behavior.
Runtime protection
Select runtime checks based on the threat model.
Configure appropriate defensive responses.
Test normal and detected conditions.
Account for legitimate use of rooted devices or emulators.
Measure runtime overhead.
Release validation
Test the final protected build.
Run regression and performance tests.
Verify application signing.
Securely retain the required mapping and configuration files.
Publish the same artifact that was tested.
Apply multiple protection layers to Android applications
R8 provides important shrinking, optimization, and baseline obfuscation for Android release builds. Applications with greater exposure may benefit from additional control-flow transformations, selected string and resource encryption, and runtime checks.
DashO Android Mode works alongside R8 to add these protection layers without modifying the original source files. The appropriate configuration depends on the application’s architecture, performance requirements, supported devices, and threat model.
No individual protection makes an application immune to reverse engineering or tampering. A stronger Android security strategy combines application hardening with secure coding, server-side controls, dependency management, signing, and testing.
Start a free DashO trial to test layered Android protection against your own application and build process.
Protect your applications with PreEmptive
Add layered obfuscation and runtime protection to your .NET, Java, Android, and JavaScript applications. Start a free PreEmptive trial to test the right protection tools against your own code and build process.
Frequently asked questions about Android app obfuscation
What is Android app obfuscation?
Android app obfuscation transforms compiled code and other application components to make them more difficult to understand or modify while preserving the application’s intended behavior.
Does R8 obfuscate Android applications?
Yes. R8 can rename classes, fields, and methods in addition to shrinking and optimizing application code. However, R8 does not provide all the protections associated with application hardening, such as DashO’s control-flow obfuscation, selected string and resource encryption, and runtime checks.
Is R8 enough to prevent reverse engineering?
No. R8 is a valuable baseline, but it does not prevent every form of static or dynamic analysis. Teams with higher-risk applications may add other protections based on their threat model.
Can obfuscation make an Android application impossible to reverse-engineer?
No. Obfuscation increases the time and effort required to analyze an application, but no client-side protection makes reverse engineering impossible. Obfuscation should complement secure design, backend authorization, signing, testing, and dependency management.
How does DashO work with R8?
In the recommended Android Gradle integration, R8 handles renaming, removal, and optimization. DashO Android Mode adds control-flow obfuscation, selected string and resource encryption, and runtime checks. R8 should remain enabled when using this integration.
Does DashO modify an application’s source code?
No. DashO works with compiled application code during the build process. Developers continue working with readable source files while the build pipeline produces protected release artifacts.
Over a typical 2 day window, we record 10 times as many emulator sessions lasting more than 30 minutes for vNext as for the legacy emulator. Our bet is that agentic development is helping drive the difference!
The appeal is straightforward: coding agents need a database they can run anywhere. They also need a database they can start, seed, test against and debug as they work. Our vNext emulator fits that workflow naturally.
A different kind of developer
With the vNext emulator, we’re building for a broader idea of the developer: a person at a keyboard, a coding agent, or the two working together. The original priorities of portability, lighter infrastructure and faster startup give us a strong foundation. Today, agentic workflows are explicitly part of how we think about the emulator’s evolution.
A coding agent doesn’t just need to write an SDK call. It needs to create the database, load test data, run the application, inspect what happened and repeat. Ideally, it can do that inside a workspace that disappears when the task ends.
Copilot’s hosted coding agent defaults to ephemeral Ubuntu; Codex Cloud creates isolated workspaces. Local tools such as Codex CLI, Claude Code and OpenClaw offer different execution and isolation options. Docker access isn’t universal, but portable local infrastructure fits many of these workflows.
Why “vNext” if it is already GA?
GA describes release status. vNext distinguishes two different emulator architectures.
The legacy emulator uses the Azure Cosmos DB service codebase and remains valuable for high-fidelity compatibility testing, particularly on Windows. Older Linux containers exist too, with platform limitations. Neither emulator reproduces every cloud-service capability.
The vNext emulator takes a lighter, portable approach to emulating common workflows on Azure Cosmos DB. Its Linux container supports Windows, macOS and Linux hosts on x64 and ARM64, with faster startup among its design goals. For short-lived jobs, startup and resource overhead are part of the developer experience, not background details.
Our longer-term direction is for vNext to replace our legacy emulator as its coverage matures. GA doesn’t mean that transition is complete. vnext-latest identifies the vNext image stream; pin a tested release or digest in CI.
Enhancements for coding agents
Recent vNext releases give agents more ways to act on a database, not just generate code for one.
Prepare fixtures without a seeding application. The bundled shell and startup scripts let an agent create test data, then inspect it non-interactively with cosmoshell.sh -c. Mount .csh scripts at /init and set ENABLE_INIT_DATA=true; initialization runs on first startup, not before every test.
Diagnose before rewriting code. With OTLP export and a collector, shell --otel correlates client and gateway spans. An agent can inspect the failing operation alongside test assertions instead of guessing from an exception. Logs and traces feed Debug / Diagnose separately from test results.
Exercise the SDK’s negotiated protocol.October’s CosmosBinary support lets agents test binary request/response paths locally; JSON remains supported. The existing HTTP/2 thin-client gateway (GW 2.0) is separate from Nacelle’s HTTP/1.1 main gateway.
Check generated container definitions. October’s stricter indexing-metadata validation helps catch configuration mistakes before deployment. It is groundwork for indexing, not full index execution or performance parity.
Explore parallel test workloads. October’s experimental Nacelle gateway addresses some concurrency issues. Try --gateway-engine nacelle when test workers share an emulator; further improvements are planned, not measured gains promised here.
Test retrieval code locally.June added vector search. An agent can use synthetic embeddings and expected result IDs to test retrieval logic, not merely produce a plausible query.
Try it in your agent’s workflow
On a Linux host with Docker access, start a headless instance:
Use gateway mode and a bounded wait on http://localhost:8080/ready. HTTP is the default; .NET and Java need the documented HTTPS/certificate setup. Stop and remove the instance with docker stop cosmos-agent.
Run your existing integration suite first. Then evaluate Nacelle separately by appending --gateway-engine nacelle and comparing the same workload. Keep fixtures and configuration fixed.
Review the release’s compatibility warning before upgrading. Keep the emulator isolated: authentication isn’t enforced, and local tests don’t replace Azure validation.
Choose one approach for your next project:
Guide your coding agent as you iterate:Azure Cosmos DB Agent Kit supplies Cosmos DB best-practice skills within your existing development workflow.
Either way, try vNext as your local test database and share feedback.
About Azure Cosmos DB
Azure Cosmos DB is a fully managed NoSQL and vector database for modern applications, including AI. Ready to move beyond local testing? Try Azure Cosmos DB for free.
Follow us on X, YouTube and LinkedIn for product updates and developer walkthroughs.
Python 3.15 support: The core framework and root agent-framework package now support Python 3.15, but not every package, optional extra, or feature supports it yet because of upstream dependency constraints. Before upgrading, check the README for each package and extra you use. The Python README summarizes the currently known limitations.
Warning
Persisted-state compatibility: This applies to persisted artifacts that contain serialized Agent Framework Content or streamed response-update records. That includes chat and message histories; session stores and protocol snapshots when they embed those records; and workflow checkpoints only when their checkpoint state contains them. Plain session metadata and checkpoints without serialized messages, content, or response updates are unaffected.
The 1.22 runtime can read records written by earlier releases, but pre-1.22 runtimes cannot read new records containing hosted or merge_mode. Do not run old and new workers against the same affected store during a rolling deployment, and do not downgrade after 1.22 has written these records unless the new state is isolated or migrated.
Added
agent-framework-azure-cosmos-memory: Add a dedicated Cosmos memory provider User-Agent token (#9223)
agent-framework-foundry: Surface OAuth consent requests in non-streaming responses (#8976)
agent-framework-ollama, agent-framework-openai: Add configurable service URLs to chat clients (#9222, #9225)
agent-framework-ag-ui: [BREAKING] Enforce fail-closed request trust boundaries and bind workflow continuations and interrupts to the resolved thread ID (#9316, #9365)
agent-framework-ag-ui: [BREAKING] Prevent client Shared State and stored Thread Snapshots from replacing tool-approval state, including when AG-UI wraps an agent that does not expose its middleware (#9368)
agent-framework-declarative: [BREAKING] Bound HTTP response bodies and compressed decoding stages with a configurable limit (#9358)
agent-framework-devui: Refresh frontend runtime and development dependencies (#9230, #9306, #9310)
agent-framework-purview: Include citations and error details in Purview policy checks (#9375)
agent-framework-core: Treat a single string passed to Message as one text content item instead of one item per character (#9327)
agent-framework-core, agent-framework-declarative, agent-framework-foundry-hosting: Discard pending workflow state after failed or interrupted execution (#8819)