How do you implement custom merge checks at scale across hundreds of Bitbucket repositories using Bitbucket Connect or Forge apps?
Quick Answer
Custom merge checks are implemented as Bitbucket Connect (iframe-based) or Forge (serverless) apps that register a merge check module. The app evaluates custom conditions (compliance checks, architecture rules, required sign-offs) and returns pass/fail status via webhook callbacks. At scale, the app must handle concurrent evaluations across hundreds of repositories with sub-second response times.
Detailed Answer
Think of custom merge checks like a specialized quality inspector on an assembly line. The factory (Bitbucket) handles the standard inspections (build passing, approvals), but your custom inspector (the Connect/Forge app) checks things specific to your organization: 'Does this change have a signed compliance attestation?' 'Does the database migration have a rollback script?' These custom rules are unique to your business and cannot be expressed through Bitbucket's built-in merge checks.
Bitbucket Connect apps are add-ons that extend Bitbucket's functionality using the Atlassian Connect framework. A merge check module in a Connect app registers an endpoint that Bitbucket calls when a user attempts to merge a pull request. The app receives the PR details (repository, source branch, destination branch, commits, diff) via webhook, evaluates its custom logic, and returns a pass or fail response. The app is hosted on your own infrastructure (or a cloud function) and must be publicly accessible via HTTPS. Bitbucket Forge apps are the newer alternative: serverless functions that run on Atlassian's infrastructure, eliminating the need to host and operate your own backend. Forge apps use the same merge check module concept but execute as FaaS (Function as a Service) on Atlassian's cloud.
The architecture for merge checks at scale requires careful design. When a developer clicks 'Merge' on any of hundreds of repositories, Bitbucket invokes the merge check endpoint. The app must respond quickly (under 10 seconds, or Bitbucket times out and shows an error). For complex checks that query external systems (JIRA compliance boards, architecture decision records, deployment calendars), the app should pre-compute results when the PR is updated (via PR webhooks) and cache the verdict, so the merge check endpoint simply returns the cached result. This pattern converts the synchronous merge-time check into an asynchronous pre-computation, ensuring fast response times even when the underlying checks are expensive.
In production, organizations deploy custom merge checks for requirements that built-in checks cannot express. Common examples include: requiring a linked Jira issue to have a specific label (e.g., 'compliance-approved') before merging changes to regulated services; enforcing that database migration files include a corresponding rollback migration; requiring that changes to shared libraries have approval from the library's owning team (checked via a CODEOWNERS-equivalent file parsed by the app); and blocking merges during production freeze windows (checked against a deployment calendar). The app typically serves all repositories in the workspace, using repository metadata (project, tags, or a configuration file in the repo) to determine which rules apply.
A gotcha that causes production incidents: merge check apps become a single point of failure for the entire development workflow. If the app goes down, no one can merge pull requests across any repository. The app must be deployed with high availability (multiple replicas, health checks, auto-scaling) and implement circuit breaker patterns. When the app cannot determine the merge check result (external system down), it should fail open with a warning rather than fail closed and block all merges. Monitoring the app's response times and error rates is critical because a slow merge check degrades developer experience across the entire organization. Forge apps partially mitigate this by running on Atlassian's managed infrastructure, but they introduce vendor dependency and have execution time limits (25 seconds for Forge functions).