Define a contract between autonomous teams

Mar 1, 2022 · 6 min read

So you have a bunch of autonomous engineering teams that are allowed to work in their own way. Fantastic. However, maybe you want to provide centralised support or build tooling that exposes things in a certain way. For example, maybe you want to build a department wide Open API documentation site for engineering teams and partners. You don’t want to enforce anything on the teams, such as making them all create specific folders in their repo. You do, however, need to know where certain files are.

One solution that a couple of us came up with over the last two years was called [acme]-json. Note, I’ve used ACME here rather than the name of the company I worked for. We created an acme.json file in the root of every git repo. This was the only mandate, the file had to exist. This single file provided a contract between all teams and automated tooling.

This single file provided a contract between all teams and automated tooling.

The project

The project had the following scope:

We defined a mission statement:

The acme-json project should be seen as a public contract between a team and the rest of the engineering department. The schema is there to expose internals (the way your team wants to work) to other teams, and tooling, so they can consistently understand information they need, without dictating an implementation to you.

The schema

The schema was iterated on over a number of weeks, by many of the engineering teams. A cut down version (for brevity and intellectual property reasons) is shown below.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://company-url.com/schemas/acme-json/v1#",
  "type": "object",
  "properties": {
    "accounts": {
      "description": "This describes all of your AWS accounts that you look after or use for this module.",
      "type": "array",
      "items": {
        "$ref": "https://company-url.com/schemas/acme-json/v1#account"
      }
    },
    "apiDocPaths": {
      "description": "An array of locations that expose your Open API documentation files.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "ci": {
      "description": "The details of your CI/CD instance.",
      "type": "object",
      "anyOf": [
        {
          "$ref": "https://company-url.com/schemas/acme-json/v1#ci"
        }
      ]
    },
    "integrations": {
      "description": "A list of integrations that consume this module.",
      "type": "array",
      "items": {
        "$ref": "https://company-url.com/schemas/acme-json/v1#integration"
      }
    },
    "moduleName": {
      "description": "The official short name of the module.",
      "type": "string",
      "anyOf": [
        {
          "$ref": "https://company-url.com/schemas/acme-json/v1#moduleNames"
        }
      ]
    },
    "productionBranch": {
      "description": "The name of the branch where your production code resides. For example, in a GitFlow scenario where you have a 'develop' and 'main' branch, this would be 'main'",
      "type": "string"
    }
  },
  "required": ["moduleName", "productionBranch"]
}

I won’t go over each item, but some call outs:

The validator

As engineers we lint and validate everything. This was no different. We wanted the acme.json file for a reason. Therefore we also needed those files to be “correct”. Therefore we created a validator that could be added to the CI/CD tooling the teams used. So, we created an npm package, written in JavaScript that validated the JSON file against the schema.

We followed semver release processes for the Schema and the Validator.

The documentation

We wanted to clearly document the intent of every attribute in the schema. So whilst we provided comments directly in the schema, we also made use of the GitHub wiki for the repo. Every attribute was explained in depth as to why it was there, what options were available, and how the attribute could be used.

Documentation can often be overlooked, but I firmly believe it can save you time and effort in the long run.

The mindset

Getting engineering teams to agree on things is hard. Getting them to do something they don’t want to do can be harder. When we started this project, there were around 300 repos in GitHub for our business. Trying to identify and co-ordinate all the owners for those repos was going to be a tall order. We agreed it would be easier to create the acme.json file, populate where we could (based on other data points at our disposal), and raise a pull request.

We therefore set off to write a load of bash scripts to:

With wide communication in the department, by the time the Pull Requests were raised, engineering teams just knew to approve and merge them.

Adoption

What did we end up using this project for? Here’s a few things:

Summary

This post provided a summary of what we did in order to allow teams to be autonomous and work in their own ways, whilst still providing enough structure to automate department wide initiatives. As with all things in life, it wasn’t as straight forward as the post makes out, but that’s half the fun isn’t it!