At Atlassian, we build software that unleashes the potential of all teams. As engineers, we know that developer experience directly impacts productivity, code quality, and team happiness.

In this blog post, I’ll share how our Mobile Platform team transformed React Native development with DevMate, an in-house CLI tool that dramatically reduces build times and streamlines environment setup. If you’ve ever switched branches in a React Native project and had to wait through a full rebuild despite only changing JavaScript files, or found yourself rebuilding after a git pull, or spent days configuring a development environment with the right versions of Node, Ruby, and CocoaPods, this story is for you.

The Challenge: React Native Development Friction

When our team embarked on migrating the Confluence Mobile app from native to React Native, we quickly discovered that the developer experience wasn’t as smooth as we had hoped. Setting up development environments was cumbersome, build times were lengthy, and the workflow was inconsistent across platforms.

The Pain Points We Needed to Solve

Migrating a complex app like Confluence from native to React Native presented several specific challenges:

  1. Environment Setup Complexity: Developers needed to manually install and configure multiple tools including Node, Yarn, Ruby, CocoaPods, Android Studio, Xcode, and more. Additionally, they had to set up authentication tokens and secrets to access internal repositories and mirror sites that host our node packages. This process was error-prone and often required assistance from experienced team members.
  2. Inconsistent Workflows: Different developers had different approaches to building and running the app, leading to inconsistent results and “works on my machine” issues. Developers often forgot to run yarn install or pod install after pulling changes, resulting in cryptic errors and wasted debugging time.
  3. Long Build Times: Although React Native supports Hot reloading for in-app JavaScript changes, many common workflows — such as switching branches, rebasing, adding native dependencies, or modifying configuration files — require native rebuilds. These rebuilds can take several minutes, significantly slowing down iteration speed.
  4. Cross-Platform Testing: Engineers needed to test changes on both iOS and Android, doubling the build time overhead.
  5. Onboarding Friction: New team members could spend several days setting up their development environment — installing tools, resolving version mismatches, and configuring paths — before they could write a single line of code.

These challenges were not only affecting our productivity but also impacting developer happiness and code quality. We needed a solution that would address all these pain points in a unified way.

The Solution: DevMate

DevMate was born from the need to solve these problems. We designed it as a single entry point for all development tasks, with a focus on automation, consistency, and speed. The core philosophy behind DevMate is simple: developers should spend time building features, not waiting for builds or troubleshooting environment issues.

🔧 Environment Setup: From Days to Minutes

One of DevMate’s core features is its comprehensive environment setup capability. The doctor command verifies and installs all required dependencies:

devmate doctor

Implementation approach: We built this as a dependency chain where each tool checks for prerequisites before installation. For example, our Ruby installation checks for GPG, sets up RVM, then installs the correct Ruby version. Each dependency implements a common interface:

interface Dependency { check(): Promise<InstallationStatus>; install(): Promise<void>; verify(): Promise<boolean>; }

This doctor command:

  • Checks for the correct versions of Node, Ruby, Yarn, CocoaPods, Xcode, Android Studio, and more
  • Automatically installs missing dependencies or updates incorrect versions
  • Configures environment variables and paths
  • Sets up Atlas tokens and secrets required to access internally hosted repositories and mirror sites for node packages
  • Verifies VPN connectivity (required for our internal repositories)

What used to take days of troubleshooting now takes minutes with a single command. New team members can be productive on their first day, and everyone works with the same correctly configured environment.

```bash # Example output from the doctor command ✅ Node 20.15.0 is successfully installed. ✅ Yarn 1.22.19 is successfully installed. ✅ Ruby 3.2.2 is successfully installed. ✅ CocoaPods 1.12.1 is successfully installed. ✅ Xcode 16.2 is successfully installed. ✅ Atlas CLI authentication configured successfully. ✅ Internal mirror site credentials verified and .npmrc configured. ⚠️ Android Studio is not installed. Installing...

🔄 Intelligent Dependency Management

Another key feature of DevMate is its ability to automatically detect when dependency installation is needed.

Implementation: We use file hashing to track changes:

# File hash monitoring triggers dependency reinstall: # package.json, yarn.lock, Podfile → yarn install + pod install (iOS) # Individual app dependencies + patch files → yarn install

The tool:

  1. Creates and compares hashes of package.json and other related files to determine if yarn install needs to run
  2. Monitors Podfile changes to determine if update is required
  3. Automatically executes these commands only when necessary

This eliminates common errors like “Module not found” or outdated native dependencies that occur when developers forget to run these commands after pulling changes or switching branches.

# DevMate automatically detects and runs necessary commands ⚙️ Changes detected in package.json, running yarn install... ⚙️ Changes detected in Podfile, running pod install...

⚡ Smart Build Skipping: From Minutes to Seconds

Perhaps the most impactful feature of DevMate is its intelligent build skipping.

Implementation: We track file modification times for native files using a hash-based approach:

// iOS: Monitor .swift, .h, .m, .mm, .plist, .entitlements files // Android: Monitor .kt, .java, .gradle, .xml files // Hash calculation: file path + modification time const nativeHash = crypto.createHash('sha256') .update(JSON.stringify(nativeFiles.map(f => ({ path: f, mtime: fs.statSync(f).mtime.getTime() })))) .digest('hex');

While React Native’s hot reloading helps during active development, it doesn’t solve the rebuild problem when switching contexts (branches, rebases) or when the Metro bundler isn’t running. DevMate goes beyond hot reloading by analysing what has changed since the last build and skipping the native build step when possible:

devmate run --ios / --android

This command:

  1. Calculates a hash of all native file’s modification dates
  2. Compares it with the stored hash from the previous build
  3. If no native changes are detected, it reuses the existing app binary and only refreshes the JavaScript bundle

Key insight: We store hashes per app/platform combination (e.g., app-name-ios.hash, app-name-android.hash) and validate that the existing binary still exists before skipping.

The result? Launch times reduced from several minutes to just few seconds when only JavaScript changes are involved. This dramatic improvement has transformed our development workflow, allowing developers to iterate much faster and switch branches without the penalty of long rebuild times. Unlike hot reloading, which only works during an active development session, DevMate’s build skipping works across sessions, branch switches, and even after computer restarts.

📊 Anonymous Usage Analytics: Data-Driven Improvements

To continuously improve DevMate and understand our developer’s needs, we implemented anonymous usage analytics that track:

  • Build times and success rates
  • Command usage patterns
  • Environment configurations
  • Failure points and error messages

This data has been invaluable in identifying bottlenecks and prioritising improvements. For example, we discovered that 78% of builds could be skipped entirely, which led us to focus on optimising the build skipping feature.

Beyond Expo CLI: Why We Built Our Own Tool

While Expo CLI and React Native CLI are popular choices for React Native development, we needed more control and native-aware features. Here’s how DevMate compares to these existing tools:

DevMate compliments these existing tools and provides a more tailored experience for our specific needs, especially when it comes to working with native code and optimising for our internal workflows. For e-g, it doesn’t replace React Native’s hot reloading but complements it by addressing the scenarios where hot reloading falls short.

Technical Implementation

Under the hood, DevMate is built with TypeScript and Bash. The architecture follows these principles:

  1. Single Entry Point: All commands go through the main CLI interface
  2. Dependency Awareness: The tool understands dependencies between components
  3. Intelligent Caching: Hashes are used to detect changes and avoid unnecessary work
  4. Platform Abstraction: Common operations are abstracted across iOS and Android
  5. Authentication Management: Secure handling of tokens and credentials for internal repositories and package registries

Key Patterns for Building Similar Tools

Dependency pattern: Each tool implements a common interface for consistent checking and installation:

interface Dependency { getInstallationInfo(): Promise<InstallationStatus> fixInstallation(): Promise<void> }

Two-tier hash-based change detection: The core innovation uses different hashing strategies:

// 1. Native files: modification time hashing (fast) class NativeFilesHasher { hashChanged(): boolean { const oldHash = this.#oldHash() return oldHash !== this.#newHash() // Delegates to bash script } } // 2. Dependencies: content hashing (accurate) class YarnHasher { hashChanged(): boolean { return !hashMatches('yarn.lock') || !hashMatches('package.json') || !hashMatches('Podfile') } }

Performance strategy: Bash scripts handle file system operations, TypeScript orchestrates:

# Native file monitoring via bash for speed find ios/ android/ -name "*.swift" -o -name "*.gradle" -exec stat -f '%Sm %N' {} +

Key insights:

  • File modification time vs. content hashing: Modification times are 10x faster for native files where exact content doesn’t matter
  • Platform-specific file patterns: iOS monitors .swift/.h/.plist ..., Android monitors .gradle/.kt/.xml ...
  • Bash delegation: File system operations in bash, orchestration in TypeScript for best of both worlds

This hybrid approach gives you the speed of native tools with the reliability and error handling of a modern runtime.

Measuring the Impact

The impact of DevMate on our team’s productivity has been substantial and measurable:

  • Onboarding time reduced from days to hours (average 3 days → 2 hours)
  • Build times reduced by up to 97% for JavaScript-only changes (8 minutes → 1.2 seconds)
  • Context switching time reduced by 95% when moving between branches (previously required rebuilds)
  • Dependency-related errors reduced by 80% through automatic detection of when to run yarn/pod install
  • Context switching minimised with a single tool for all tasks
  • Consistency improved across developers and environments
  • Developer satisfaction significantly increased (measured in internal surveys)

One developer noted: “DevMate has completely changed how I work with React Native. What used to be a frustrating experience with long wait times is now smooth and efficient.”

We’ve also seen a direct correlation between the introduction of DevMate and increased velocity in our development cycles. Teams are shipping features faster and with fewer environment-related bugs.

Looking Ahead: The Future of DevMate

While DevMate has already transformed our development workflow, we have ambitious plans for its future:

  1. Open-Sourcing: We’re exploring the possibility of open-sourcing DevMate to benefit the broader React Native community.
  2. Deeper CI Integration: Enhancing our CI pipelines with DevMate’s intelligence to speed up builds and tests.
  3. Cross-Platform Improvements: Further streamlining the experience of developing for both iOS and Android simultaneously.
  4. Performance Optimisation: Continuing to identify and eliminate bottlenecks in the development workflow.

Key Takeaways

The journey from native to React Native presented significant challenges for our team, but it also gave us the opportunity to rethink and optimise our development workflow. DevMate has become an essential tool in our development arsenal, dramatically improving productivity and developer happiness.

By focusing on automation, intelligence, and speed, we’ve created a tool that allows our developers to spend less time waiting for builds and more time delivering features. While DevMate is currently an internal tool, we’re excited about the possibility of sharing it with the broader community in the future.

If you’re building a React Native app at scale, we encourage you to think about your developer experience holistically. Tools like DevMate demonstrate that investing in developer experience pays dividends in productivity, code quality, and team satisfaction.


About the author: This blog post was written by the Mobile Platform Team at Atlassian. DevMate is an internal tool that is not yet open-sourced. The team continues to develop and improve DevMate for internal use.

DevMate: Accelerating React Native Development at Atlassian