Gradle

5 posts

line4 min readCurated summary

Android CLI for AI Agents: Applying It to Large-Scale Mobile Development Environments

LINE’s Android app is a large monorepo with hundreds of Gradle modules and developers, making unrestricted AI-agent searches expensive and unreliable. Generic tools such as `grep` and `glob` often return excessive, semantically weak results, causing agents to waste tokens and retry. The team therefore built thin wrappers, skills, and prompts around Android CLI to provide efficient documentation lookup and Android Studio’s semantic capabilities across multiple agents. ## Why Generic Search Breaks Down at Scale - Large repositories can return huge numbers of search results from a single request. - Search output consumes agent context and increases costs. - Text search cannot reliably answer semantic questions such as: - Where a symbol is declared or used - Whether a file contains IDE-detectable problems - Whether code is unused - As the number of modules grows, agents are more likely to rely on irrelevant results and repeat searches. ## Replacing MCP Documentation Search with Android CLI - The team first adopted Android CLI for official documentation search. - It provides current documentation for Android, Jetpack Compose, AndroidX, Firebase, and related technologies. - This helps reduce hallucinations caused by outdated pretrained knowledge. - Android CLI’s `docs` commands are exposed through the `get-android-dev-knowledge` skill: - `docs search` finds relevant documentation. - `docs fetch` retrieves the document body from its Knowledge Base URL. - Compared with the previous Google Cloud Knowledge MCP setup, Android CLI eliminates: - Per-developer Google Cloud authentication - An authentication proxy - Quota-management and workaround logic - The result is fresher documentation with fewer tokens and less supporting infrastructure. ## Bundling the Android CLI Binary The team stores the Android CLI binary in the repository and invokes it from a fixed path such as `.agents/tools/android-cli/android`. - **Consistent environments** - Developers, CI systems, and agent hosts use the same pinned version. - Installation differences in version, path, and platform are reduced. - **Security enforcement** - The wrapper automatically adds `--no-metrics`. - This prevents agents from accidentally omitting the company-required telemetry setting. - A fixed binary location makes reliable wrapper enforcement possible. - **Manageable repository cost** - Existing use of Git LFS makes storing the binary relatively inexpensive. ## Handling the Android CLI Metrics Bug - Android CLI 1.0 initializes metrics tracking before honoring `--no-metrics`. - It may still attempt to write under `~/.android/cli`. - In restricted sandboxes, this causes a multi-page Java stack trace, wasting agent context. - The wrapper now probes write access before invoking the CLI: - It creates `~/.android/cli`. - It attempts to create a temporary probe file. - If writing is blocked, it emits a concise, parseable error explaining the required permission. - This converts a noisy failure into an actionable one-line message. ## Android Studio Integration Android CLI 1.0 added integration with running Android Studio instances, enabling IDE-level semantic operations from the command line. - `studio check` - Verifies that Android Studio is running. - Confirms that the target project is open and indexing is complete. - `analyze-file` - Runs IDE inspections on a single file without a build. - Detects semantic issues such as unused code. - `find-declaration` - Locates symbol declarations in the project and inside `.aar` or `.jar` dependencies. - `find-usages` - Finds references to a symbol. - `render-compose-preview` - Renders Compose `@Preview` functions as PNG images. ## Wrapping Studio Features as Skills - The team does not expose raw Android CLI behavior directly to agents. - Each capability is wrapped in a lightweight script and presented as an agent skill. - The first skill created was `studio-check`. - This follows the same design used for documentation search and ensures failures are concise, predictable, and easier for agents to interpret. ## Practical Recommendation For large Android repositories, use Android CLI behind repository-pinned wrappers and agent skills rather than exposing generic search or raw CLI commands directly. Enforce security flags, validate filesystem prerequisites early, and prefer IDE-backed semantic operations when agents need declarations, usages, inspections, or Compose previews.

Read original(opens in new tab)
gitlab4 min readCurated summary

GitLab 19.0 | GitLab Docs

GitLab 19.0, released May 21, 2026, expands AI-assisted development, work-item customization, secrets management, and dependency security. Major changes include group-level Duo review instructions, configurable work-item types, open beta access to GitLab Secrets Manager, and generally available SBOM-based dependency scanning. GitLab also introduces usage-based billing for Duo Core and adds new agent, search, model, and workflow capabilities. ## Customization and Project Management ### Group-level custom review instructions for GitLab Duo - Premium and Ultimate feature for GitLab.com, Self-Managed, and Dedicated. - Groups and subgroups can share review guidance through: ```text .gitlab/duo/mr-review-instructions.yaml ``` - A project in the group serves as the template. - Group instructions are combined with project-specific instructions. - Supported by both Code Review Flow and GitLab Duo Code Review. ### Configurable work item types - Projects can define custom types such as User Story, Bug, or Maintenance instead of using only Issues and Tasks. - Each type has its own name and icon. - Types support custom fields, status lifecycles, saved views, and issue boards. - Configuration at the top-level group or organization cascades to projects. - Administrators can enable or disable types globally or allow project-level control. - Disabling a type does not affect existing work items. ## Security and Dependency Management ### GitLab Secrets Manager enters open beta - Available to Premium and Ultimate customers on GitLab.com and GitLab Self-Managed. - Project and group Owners can store and reference CI/CD secrets in GitLab. - Secrets are scoped to projects or groups and available only to jobs that explicitly request them. - The feature remains subject to beta support policies and may not be production-ready. ### SBOM-based dependency scanning becomes generally available - Available for Ultimate customers across GitLab offerings. - Maven, Gradle, and Python projects receive visibility into transitive dependencies and their vulnerabilities. - Automatic dependency resolution runs when no lockfile or dependency graph is available. - If resolution is unavailable, manifest scanning examines direct dependencies in files such as: - `pom.xml` - `requirements.txt` - `build.gradle` - `build.gradle.kts` - Manifest scanning is enabled by default, while full transitive coverage requires dependency resolution, a lockfile, or a manually supplied dependency graph. ## GitLab Duo and Agentic Development ### Duo Developer enhancements - GitLab Duo Developer can be triggered by: - Assigning it to an issue - Selecting **Generate MR** - Mentioning it with `@mention` in an issue or merge request discussion - It can turn feedback, to-do items, and design questions into code changes, follow-up merge requests, or research summaries. - With `AGENTS.md` and `agent-config.yml`, it can run tests and checks before committing. - Administrators can enable mention and assignment triggers for eligible projects. ### Duo Core adopts usage-based billing - Code Suggestions in the Web IDE and desktop IDEs now consume GitLab Credits. - Duo Chat becomes agentic for Duo Core users and runs on the GitLab Duo Agent Platform. - Administrators must enable the Agent Platform for the instance or top-level group to use Chat in GitLab or desktop IDEs. ### New agent and search capabilities - Exact code search supports repository filtering with the `repo:` syntax: ```text def authenticate repo:my-group/my-project ``` - Flows and external agents can trigger when a draft merge request is marked ready for review. - The merge request ready trigger is controlled by the `merge_request_ready_flow_trigger` feature flag and is disabled by default. - Claude Opus 4.7 is available in the Duo Agent Platform for complex, multistep tasks involving code review, CI/CD, and vulnerability resolution. - GitLab Duo Agent Platform Self-Hosted adds compatibility with Gemini models and supports multiple flows, including Code Review Flow and SAST vulnerability workflows. GitLab 19.0 is particularly significant for teams adopting AI agents and centralized development governance. Organizations should review Duo’s new billing model, test Secrets Manager carefully during its beta period, and enable dependency resolution to obtain comprehensive vulnerability coverage.

Read original(opens in new tab)
netflix4 min readCurated summary

Scaling ArchUnit with Nebula ArchRules

Netflix’s Nebula ArchRules extends ArchUnit so architectural and API-lifecycle rules can be shared across thousands of Gradle repositories. Unlike AST-based tools, ArchUnit analyzes compiled JVM bytecode, supports multiple JVM languages, and offers a type-safe Java API for authoring and testing rules. The approach helps identify unsafe API usage, technical debt, and deviations from Netflix’s preferred development practices at fleet scale. ## The API Lifecycle Problem - Netflix operates tens of thousands of Java repositories in a polyrepo environment. - A library incident involving a backwards-incompatible change highlighted the difficulty of deciding when deprecated APIs can safely be removed. - Netflix introduced lifecycle annotations: - `@Deprecated` for APIs scheduled for removal - `@Public` for APIs intended for downstream use - `@Experimental` for APIs that may change - Unannotated APIs are treated as internal - The remaining challenge was identifying downstream projects that use internal, experimental, or deprecated APIs incorrectly. - The same tooling could support large migrations, such as major Spring Boot upgrades. ## Why ArchUnit - ArchUnit is an open-source library commonly used within JUnit suites to enforce architectural rules. - It is built on ASM and analyzes compiled JVM bytecode rather than source syntax. - Its main strengths are: - Cross-language JVM support for Java, Kotlin, Scala, and other JVM languages - A fluent builder API for readable rule definitions - A lower-level API for complex custom analysis - Access to class relationships, dependencies, and call sites through its class graph - Standard ArchUnit is primarily designed for one repository, so Netflix created Nebula ArchRules to distribute rules across many Gradle projects. ## Bytecode Analysis vs. AST Analysis - AST-based tools such as PMD inspect source-code structure and can be sensitive to language-specific syntax and syntactic sugar. - Supporting multiple JVM languages may require separate rules for each language. - ASM analyzes the bytecode that will actually execute, regardless of how the source was written. - This makes rules more consistent across Java, Kotlin, Scala, and other JVM languages. ## Rule Authoring - Tools such as PMD and SpotBugs are generally optimized for built-in rules or third-party plugins rather than custom rule development. - PMD custom rules may require difficult-to-maintain XPath expressions and separate tooling for testing. - ArchUnit rules are written as type-safe, fluent Java code. - Rules can be unit tested directly by passing them class references, without running a separate analysis process. - ArchUnit’s class graph provides contextual information about dependencies and call relationships, enabling more sophisticated checks. ## ArchRules Libraries - The Nebula ArchRules Library Plugin adds an `archRules` source set to a Gradle project. - A class implementing `ArchRulesService` exposes a `Map<String, ArchRule>`: - The map key names the rule. - The `ArchRule` defines the constraint using ArchUnit’s API. - Rule code and its dependencies are kept separate from the application’s main code. - Gradle publishes the rules in a separate JAR using the `arch-rules` classifier and an `arch-rules` usage attribute. - Downstream projects must use Gradle Module Metadata to resolve the rules variant. ## Standalone and Bundled Rule Libraries - Standalone rule libraries contain only `archRules` code. - They are useful for: - Enforcing rules around APIs the organization does not own - Checking usage of Java or open-source libraries - Applying generic rules, such as prohibiting use of deprecated APIs - Bundled rule libraries contain both normal library code and rules specific to how that library should be used. - Netflix maintains open-source standalone rule libraries as examples and reusable building blocks. Nebula ArchRules turns ArchUnit from a repository-local testing library into a reusable organization-wide policy mechanism. Teams can publish rules as Gradle artifacts and apply them consistently across JVM projects, making API governance, dependency policies, and architectural standards easier to enforce at scale.

Read original(opens in new tab)
kakao4 min readCurated summary

From Student to Developer: Learning Server Flow from Lotto Implementation to Legacy Improvement

The post describes Kakao’s 2026 server-engineering onboarding program, which turns uncertainty into practical understanding through structured implementation, testing, and refactoring. Rather than supplying fixed answers, the program repeatedly asks developers to explain their design decisions and assess what their tests protect. Its central lesson is that server development becomes manageable when engineers build clear reasoning, maintainable structures, and safe change processes. ## Onboarding Through Three Stages - The program follows a progression: 1. TDD- and OOP-based implementation 2. Acceptance testing for legacy code 3. Refactoring legacy code - The focus is not only on what to build, but on how to make engineering decisions. - Core goals include: - Designing maintainable structures - Analyzing and safely improving legacy systems - Collaborating effectively, including responsible AI usage - Although originally designed for server developers, the program expanded to frontend, Android, and iOS engineers because engineering principles apply across technology stacks. ## Learning Through Questions and Collaboration - Participants were repeatedly asked: - Why was this design chosen? - Does this object truly own this responsibility? - What behavior does this test protect? - Daily meetings, pair programming, troubleshooting discussions, and PR reviews made development a collaborative activity. - The program aimed to develop engineers who could explain and defend their designs, rather than merely produce working code. ## Mission 1: Building a Lottery Game with TDD and OOP - The first assignment implemented: - Automatic and manual lottery purchases - A fixed ticket price of 1,000 won - Winning-statistics calculations - Constraints encouraged better design: - One level of indentation - Methods limited to 10 lines - Primitive values wrapped in value objects - First-class collections - Avoiding `else` through early returns - TDD required tests to be written before implementation. ### Making Randomness Testable - Random lottery-number generation initially made tests unpredictable and tightly coupled to concrete implementations. - The solution was to: - Introduce a number-generation interface - Inject the generation strategy - Create a separate test generator - This made test results controllable and encouraged a more flexible design. ### Considering Value Objects and Caching - The team also questioned whether identical number values should always create new objects. - This led to discussions about caching and the difference between object identity and value equality. - The main lesson was to evaluate design decisions, not just make the feature work. ## Mission 2: Writing Acceptance Tests for Legacy Code - Participants first protected the existing system before modifying it. - Tests focused on externally observable behavior: - User actions - System responses - State changes - Strong assertions verified not merely that an operation succeeded, but that it produced the correct result. - Cucumber-based BDD expressed scenarios in a form understandable to non-developers, treating tests as shared specifications. ### Achieving Production Parity - To avoid “works on my machine” problems, the test environment was aligned with production: - PostgreSQL replaced H2 - Docker standardized execution environments - Gradle tasks automated test execution - Test-data isolation used: - Reverse-order foreign-key deletion - `TRUNCATE ... CASCADE` - Shared cleanup utilities - These measures ensured tests started from consistent, independent states. ## Mission 3: Refactoring Legacy Code Safely - The final mission treated refactoring as training in decision-making, not simply an exercise in clean code. - The central rule was to separate structural and behavioral changes: - Structural changes must preserve behavior. - Behavior changes must avoid unrelated structural modifications. - PR reviews helped identify unintended behavior changes and taught participants to predict and control the effects of modifications. - AI was used during refactoring to accelerate broad code changes, but large changes were difficult to verify, highlighting the need to control scope and validate changes carefully. The onboarding’s practical recommendation is to approach server development through small, explainable decisions: write controllable tests, protect legacy behavior before changing it, separate refactoring from feature changes, and use AI as an assistant rather than a substitute for engineering judgment.

Read original(opens in new tab)
airbnb4 min readCurated summary

Migrating Airbnb’s JVM Monorepo to Bazel

Airbnb migrated its tens-of-millions-of-lines JVM monorepo from Gradle to Bazel over 4.5 years, achieving faster builds, testing, IntelliJ syncs, and development deployments. The move was driven by Bazel’s scalable remote execution, hermetic builds, and ability to provide shared infrastructure across Airbnb’s language-specific repositories. A gradual rollout, extensive automation, and close collaboration with service teams were central to making the migration successful. ## Results of the Migration - Build CSAT increased from 38% to 68%. - Local build and test times became 3–5 times faster. - IntelliJ syncs became 2–3 times faster. - Development-environment deployments became 2–3 times faster. ## Why Airbnb Chose Bazel ### Faster Builds Through Remote Execution - Large Gradle builds frequently took more than 20 minutes locally, while pre-merge CI builds had a p90 of 35 minutes. - Gradle had already been optimized with powerful machines and build sharding, but sharding caused underutilization and duplicated shared work. - Bazel’s cacheable actions and remote build execution enabled thousands of actions to run in parallel on short-lived workers. - “Build without the Bytes” reduced the amount of build output developers needed to download. - Bazel analysis runs in parallel, unlike the often single-threaded configuration phase of large Gradle projects. - Remote execution also improved local build performance, not just CI performance. ### More Reliable and Reproducible Builds - Gradle tasks could access the entire filesystem, creating accidental dependencies and race conditions. - Bazel sandboxes expose only declared inputs to each action, preventing undeclared files from affecting builds. - Bazel’s remote execution runs actions in identical containers with strict resource limits. - Using remote execution for both local and CI builds reduced differences between developer and CI environments. ### A Shared Build Infrastructure Layer Because Airbnb’s web, iOS, Python, Go, and JVM repositories all use Bazel, the company could standardize infrastructure for: - Remote caching - Remote build execution - Affected-target calculation - Build Event Protocol instrumentation and logging ## Starting with a Proof of Concept - Airbnb first migrated Viaduct, a large GraphQL monolith platform. - Viaduct was selected because it was complex, had slow builds, affected roughly 300 product engineers monthly, and had an infrastructure team willing to collaborate. - Bazel and Gradle initially coexisted, allowing developers to choose either system. - The team ported Viaduct’s build logic and created an automated Bazel build-file generator because the Gradle dependency graph continued to change. - Although Bazel was initially 2–4 times faster locally, developers did not adopt it immediately. - The team spent several additional months fixing missing integrations and bugs before Viaduct engineers voluntarily switched. ## Scaling Across the JVM Monorepo - Airbnb expanded breadth-first, aiming to make the entire repository compile and test under Bazel. - Gradle and Bazel continued to coexist during the migration. - This allowed developers to use Bazel locally while deployments still relied on Gradle. - Gradle provided a fallback when Bazel infrastructure, such as remote caching or execution, experienced incidents. - Maintaining two build graphs was costly, so Airbnb invested heavily in automation rather than requiring developers to maintain Bazel files manually. ## Automated Build-File Generation - The generator was inspired by Gazelle but was built internally to meet stricter performance requirements and handle dependency cycles. - It parses Java, Kotlin, and Scala source files to identify packages, imports, and symbol declarations. - These relationships are used to construct a file-level dependency graph. - Since generation ran on every commit before merging, Airbnb added external caching to keep it fast. - CI publishes a cached repository index for each mainline commit, allowing the generator to rescan only directories changed since that commit. Airbnb’s experience suggests that a large build-system migration is most effective when introduced incrementally: prove the benefits on a representative service, automate maintenance, preserve a fallback during rollout, and address developer workflow issues before expanding across the organization.

Read original(opens in new tab)