Teams

This gem is a simple, low-dependency, plugin-based manager for engineering teams within a codebase.

Usage

To use teams, add YML files in config/teams that start with structure: config/teams/my_team.yml

name: My Team

teams leverages a plugin system because every organization's team practices are different. Say your organization uses GitHub and wants to ensure every team YML files has a GitHub owner. To do this, you create a plugin:

class MyGithubPlugin < Teams::Plugin
  extend T::Sig
  extend T::Helpers

  GithubStruct = Struct.new(:team, :members)

  sig { returns(GithubStruct) }
  def github
    raw_github = @team.raw_hash['github'] || {}

    GithubStruct.new(
      raw_github['team'],
      raw_github['members'] || []
    )
  end

  def member?(user)
    members = github.members
    return false unless members

    members.include?(user)
  end

  sig { override.params(teams: T::Array[Teams::Team]).returns(T::Array[String]) }
  def self.validation_errors(teams)
    errors = T.let([], T::Array[String])

    teams.each do |team|
      errors << missing_key_error_message(team, 'github.team') if self.for(team).github.team.nil?
    end

    errors
  end
end

After adding the proper GitHub information to the team YML:

name: My Team
github:
  team: '@org/my-team
  members:
    - member1
    - member2

1) You can now use the following API to get GitHub information about that team:

team = Teams.find('My Team')
MyGithubPlugin.for(team).github

2) Running team validations (see below) will ensure all teams have a GitHub team specified

Your plugins can be as simple or as complex as you want. Here are some other things we use plugins for:

  • Identifying which teams own which feature flags
  • Mapping teams to specific portions of the code through code_ownership
  • Allowing teams to protect certain files and require approval on modification of certain files
  • Specifying owned dependencies (ruby gems, javascript packages, and more)
  • Specifying how to get in touch with the team via slack (their channel and handle)

Configuration

You'll want to ensure that all teams are valid in your CI environment. We recommend running code like this in CI:

require 'teams'
errors = ::Teams.validation_errors(::Teams.all)
if errors.any?
  abort <<~ERROR
    Team validation failed with the following errors:
    #{errors.join("\n")}
  ERROR
end

Contributing

Bug reports and pull requests are welcome!