Executable Conventions

dm03514
Dm03514 Tech Blog
Published in
5 min readOct 17, 2017

Executable Conventions codify documentation in executables. It enables enforcing documentation remains up to date, and is a straightforward change which reduces the need for documentation, lowers on-boarding effort, and minimizes context switching overhead all while simplify infrastructure tooling.

Problem

Have you ever started with getting on boarded to a new project, and while trying to run or build or execute the tests locally the documentation you are following was incorrect? Or have you ever needed to make a bug fix, or add a feature to, a project you have touched in months, or years and were unable to remember how to start the project or run the tests? In 8 years I have yet to work on a project with completely up to date and accurate documentation. It requires a serious amount of effort to keep documentation in a useful up-to-date state.

Often the only absolutely faithful place to go to see how tests are executed, or a project is built, or which system dependencies are necessary to run a service, are the tools that perform those actions (CI jobs, build jobs, build commands, etc)

If these tools are working, if the service is running, then the necessary requisites are implicitly met. This pattern aims to formalize those commands by creating a convention around them, and centralizing those commands in the project repository. Creating executable conventions will have a significant impact on on-boarding, dev workflow, CI tooling, CD tooling, and documentation.

Executable Conventions

Executable Conventions establish a cross service (org wide) convention on how to build, manage integration dependencies, execute tests, and deploy. This pattern is much like having an abstract base class which represents the convention, and then each service/project implements that convention. This way an engineer only needs to learn about the convention one time, and then is able to operate any project.

Executable Conventions:

  • Must use a language agnostic tool to implement the convention
  • Requires each service must adhere to the convention
  • Requires each service to have service/language specific conventions, but even those should be shared to the maximum possible. For example the go services I work on have a make build command, which may not make sense for an interpreted language, but all go services, within our ecosystem, have a make build command

What this pattern isn’t:

  • In the orgs I’ve worked for, this pattern has NOT been responsible for getting a local development system packages installed. But this could be as easy as having a provision-dev-env which will literally install the system packages through your OS’ package manager

The specific pattern I’ve been using for the last one and a half years, spanning multiple different technologies, uses make and docker-compose.

The Executable Convention is encoded in a Makefile, which is located in every project repo:

Next we’ll discuss how this pattern impacts Onboarding, CI, CD, Devops,

Convention as Documentation

Service conventions allow developers to learn conventions once, then apply them everywhere. Additionally, by building tooling around the conventions the conventions are easily enforced: if a project wants to use the default tooling, it HAS to adhere to the conventions.

The file above is located in every single project repo. Because of its size, it’s easy to internalize the relatively small number of service conventions. Once done it becomes second nature on how to initialize, provision, develop, test, and lint a project. No longer do the steps to starting a service or steps to running the tests need to be listed in a README file. Developers know to look at the test-unit to see how to run this projects/languages tests. All dependencies are implicitly documented in the start-dev-env command. In my 1.5 years experience this has been invaluable to understand exactly where to go in each service to see how it’s started, what its dependencies are, how it’s executed, built, etc. Since CI tooling is built around these commands, they are an up-to-date reflection of how to operate the service.

On-boarding

Formalizing convention as an executable is an amazingly powerful way to support single command environment setup. Services that support it are trivial to setup. At my current position there are a number of services which support executable conventions and a couple which don’t. The effectiveness of executable convention can be seen when new developers start. Developers that work on the services that support executable conventions are running tests in minutes, while those that work on the services that don’t can spend days going through documentation and getting their environments setup.

While working in a system of ~40 services, having executable conventions are crucial for fixing bugs on in frequently changed services. When the service adheres to the conventions, cloning the repo setting up dev dependencies, and executing tests are the same handful of commands regardless of project or language.

CI

Executable Conventions allows CI tooling to become more generic. Since all projects adhere to a contract, the CI tooling only needs to call into those projects. It’s a crucial step in empowering teams to control and modify their services, by enabling teams to easily control the commands that CI will execute.

CI Build tools (jenkins, travis, etc) are very prone to having lots of logic contained in the build jobs. This creates a really large feedback loop when setting up or modifying CI jobs. Have you ever had to modify a CI Job and ended up in time waste spiral of: make a change to build job, rebuild in tool, make a change, rebuild, make a change, rebuild...

Jenkins is especially prone to having many builds jobs with lots of bash. Having Executable Conventions allows teams to implement the specific commands in anything that they want since the CI only calls into the Makefile.

By having the CI system clone and execute methods IN the service repo it:

  • Removes complexity from the CI build jobs and puts it closer to the developer
  • Allows the critical parts of the jobs to be in version control in the repo
  • Allows the CI tooling to become more generic, by allowing generic CI templates that can be applied to any service which adheres to the convention
  • Removes duplication which is prone to arise between the way developers run tests, ie perhaps locally do not generate coverage reports, and the way that CI executes tests, by empowering developers to execute tests in the exact same way that CI will execute them
  • Reduces feedback loop when creating/updating CI commands by allowing developers to run and verify them locally instead of having to develop within the context of the CI pipeline

CD

Having deployment steps as executable conventions provides many benefits:

  • Democratizes infrastructure commands by placing them in the repo, where developers interact with them
  • Easy to find the exact commands necessary to deploy a service by tracing them through the Makefile
  • Deployment commands under version control

Closing

Executable conventions are an easy way to create living documentation, directly addressing the major issue of documentation maintenance overhead by creating tooling and practices around conventions, ensuring that they will be complied with. Executable conventions also provide a low barrier to entry to start using:

  • Define which commands services within your org should adhere to
  • Choose a technology to implement the Executable Conventions in (ie make)
  • Choose an easy convention (ie make static-analyis) and begin to implement it in each service

They can be used to create shared terminology and to significantly lower the barrier to entry to developing, testing, and deploying services!

--

--