At WorkTango, a pull request (PR) moves through a predictable lifecycle:
- Developer opens a PR against the main branch (
master, in our case) - CI runs (tests, linting, type checking)
- At least one approving review required (more for certain files via
CODEOWNERS) - All status checks must pass
- When ready, the developer adds a GitHub label (
breakdance) to request auto-merge - On merge: commits are squashed into a single commit
- Commit title = PR title (must contain a Jira ticket, e.g.,
WT-12345) - Commit description = extracted from the PR body’s “Commit Message” section
This keeps our git history clean: one commit per PR, meaningful messages, traceable to Jira.
The challenge was automating steps 5 through 8 reliably, with visibility into what’s happening.
Mergify handled this well for years. But in 2025, we wanted something we could see inside, tweak to our exact workflow, and didn’t cost anything beyond the CI minutes we were already paying for.
So we replaced it with a script and a GitHub Action. At WorkTango, we call it Breakdance.
The name is on-brand for us, but it’s also a reference to how the merge process works. One pull request steps into the circle at a time. It does its thing: CI runs, checks pass, merge happens. Meanwhile, everyone else waits their turn. Then, the next PR steps in. One at a time, each getting their moment.
Why we left Mergify
Mergify is a solid product. It auto-merged our PRs for years. Configuration was declarative and mostly intuitive. Support was responsive when we had questions.
So why move on?
We weren’t using the advanced features. Mergify has merge queues, automatic rebasing, smart scheduling, conditional rules. We used… automatic rebasing and labels.
We wanted to see inside the box. When something didn’t merge, the debugging experience was: check the Mergify dashboard, squint at logs, wonder if it was a race condition or a config issue.
We wanted to customize. Our PR title validation and commit message extraction relied on their template system. It worked, but we had ideas that didn’t fit their config model.
CI time is cheap. GitHub Actions minutes are essentially free for our usage. The Mergify subscription wasn’t expensive, but free is hard to beat when you’re not using premium features.
Ultimately, we only really needed a script that runs after a PR’s status checks pass, updates the PR if it’s behind our main branch, merges if it’s ready, and posts a comment if there’s a problem. That’s it.
Not a process worth paying to automate, at least at our scale.
How it works
When triggered, Breakdance processes all labeled PRs. The flow:
flowchart TD
A[Fetch all open PRs] --> B[Filter to labeled PRs]
B --> C{For each PR}
C --> D{Is Draft?}
D -->|Yes| E[Remove label, skip]
D -->|No| F{Valid title?}
F -->|No| G[Post error comment]
F -->|Yes| H{Valid body?}
H -->|No| I[Post error comment]
H -->|Yes| J{Mergeable state?}
J -->|behind| K[Rebase branch, skip]
J -->|dirty| L[Post conflict error]
J -->|blocked| M[Skip - wait for CI/reviews]
J -->|clean/unstable| N[Extract commit message]
N --> O[Squash merge to master]
When title or body validation fails, we post a comment explaining exactly what’s wrong—with a hidden HTML signature (<!-- breakdance-bot -->) so we can find and update our own comments later.
We then extract the ## Commit Message section from the PR body. The PR with the title “WT-12345 - Add the thing” with the following body:
|
|
Becomes the following commit message:
|
|
Every destructive action checks a dryRun flag to iterate against this on real PRs in production before going live. We caught several bugs without breaking anything. HIGHLY recommend you do the same.
The thing that usually scares people is the automatic rebasing. But it’s not scary when we only do it when a PR is ready to merge, and the rebase is clean. If it’s a dirty rebase, we force the engineer to rectify it before we can merge.
Triggering
Most of our CI jobs run in CircleCI, but Breakdance runs in GitHub Actions for security reasons.
Our CircleCI jobs are read-only by design and cannot modify the remote in any way (including GitHub things like labels). That’s a deliberate boundary erected by our SRE team that we didn’t want to compromise.
So we set up a handoff: after all checks pass, CircleCI makes an API call to trigger the Breakdance script in GitHub Actions. Most PRs merge within seconds of CI going green.
We also have a scheduled fallback—every 10 minutes during business hours, and hourly overnight. If something goes wrong with the API trigger, the worst case is a short delay.
Was it worth it?
So far so good. It’s been a year of reliably merging PRs for us.
We gained full visibility into why a PR did or didn’t merge; customization we couldn’t get from a config file; zero cost beyond CI minutes; about 500 lines of TypeScript we fully understand and can modify as needed; and one fewer third-party tool involved in our SDLC.
What we gave up: merge queues (we don’t use them) and Mergify’s nice dashboard.
Should you build your own?
If you’re not using advanced features, you want full control, and CI time is cheap, then probably yes. The whole thing took a few hours to build and has been essentially maintenance-free for almost a year now.
If you need merge queues or sophisticated scheduling, or your team doesn’t have bandwidth to own another tool, stick with a provider like Mergify. It’s fine software.
There’s also a middle ground: GitHub’s native auto merge feature and merge queues exists now. If you just need “merge when ready,” that might be enough without any code.
Other version control tools like GitLab and Bitbucket have similar features.
The code
In TypeScript (~500 lines), with proper interfaces and types:
|
|
And, in Vanilla JavaScript (~200 lines), if you prefer:
|
|