Back to all posts

How We Made Danger JS Work Better for Our Teams

At Tapsi, we have multiple frontend teams (or "tribes") working on different parts of our products. One thing we all have in common? We deal with merge requests that need to follow some basic rules, like:

  • Does a new feature has tests?
  • Did you update the changelog?
  • Is this MR too big?
  • Did you forget to assign reviewers?

We've been using Danger JS to automate these checks in our CI pipelines. But after a while, our dangerfile.ts started to look... messy. Different teams had different rules, the logic was scattered, and nobody really wanted to touch that file. Sound familiar?

The Problem

Danger JS is great for small projects or teams with simple needs. But in a company with multiple teams, each with their own standards, it quickly becomes a maintenance nightmare.

We needed something:

  • Modular
  • Easy to extend
  • Flexible enough that each team could plug in their own rules

Plugin-Based Framework

We open-sourced it here if you want to check it out: 👉 https://github.com/Tap30/danger

Basically, we wrapped Danger JS with a plugin system — inspired by how tools like ESLint or Vite let you extend functionality cleanly.

What This Looks Like

Instead of dumping everything in dangerfile.ts, you write self-contained plugins:

const mergeRequestSizePlugin = createPlugin<{ size?: number }>(
  (client, options) => {
    const threshold = options?.size || 200;
    const filesChanged = [
      ...client.git.modified_files,
      ...client.git.created_files,
    ];

    if (filesChanged.length > threshold) {
      client.warn(
        `Whoa, that's a big MR: ${filesChanged.length} files changed!`,
      );
    }
  },
);

Then in your dangerfile.ts:

import danger from "danger";
import DangerClient from "@tapsioss/danger";

const dangerClient = new DangerClient(danger);

dangerClient
  .use(mergeRequestSizePlugin, { size: 100 })
  .use(yourOtherPlugin)
  .use(yetAnotherPlugin);

dangerClient.analyze();

Simple, clean, and easy to read.

Why This Was a Game-Changer for Us

Teams Became Self-Sufficient

Each tribe could write and maintain their own plugins without worrying about breaking someone else's checks. (Shared plugins are published as packages in organization's registery.)

Better Visibility

Instead of scrolling through hundreds of lines of conditionals, you just look at the plugin list to know what's being checked.

Faster Onboarding

New devs didn't have to understand "the giant Dangerfile" anymore. They could just peek at the plugin folder and see real examples.

Easier to Test

Since plugins are just functions, writing unit tests for them was super straightforward.

The Impact

Before this, people would often ignore the Danger checks because they weren't sure what was running or why it failed. Now:

  • The rules are clear.
  • Teams have ownership of their checks.
  • And the CI feedback actually helps, rather than annoys.

What's Next?

I'm thinking about:

  • Adding conditional plugins (like: run this check only if the MR touches certain files)
  • A CLI to list all available plugins with descriptions
  • Maybe caching results between runs to make it faster

Wanna Try It?

It's open source: 👉 https://github.com/Tap30/danger