View Source About Harness

There are many ways to describe harness. Harness:

  • simplifies writing tiny isolated services by dynamically generating unecessary boilerplate files
  • makes it easy to version your practices
  • lets you make a library out of anything!

But overall, harness is really about one thing: service chassis (plural).

Services Chassis

What's a service chassis? A service chassis is a collection of tooling that sets the bar for what makes one of your services one of your services. For example, in our Elixir shoppe, we have a service chassis that includes:

And more (I can't give all our secrets away, can I? ;).

For these, we need a .tool-versions, a .credo.exs, a coveralls.json, a rel/config.exs and rel/.formatter.exs, and configuration and in-application-space code changes for Appsignal. Seems like a lot? Imagine all the other configuration I'm not even mentioning like private tooling and Dockerfiles!

If you have one service running your business, this isn't too big of a deal. One times the aforementioned number of configuration files isn't too bad. It's manageable. But what about if you have 10 services? 50? 100?

Enter static code generation tooling

If your organization employs a micro-service architecture and you've developed more than 20 services, you have probably used a static code generation tool. They're great! Plug in X Y and Z like the repository name, application name, and secret key-base, and you can have a fresh phoenix app up and running in no time!

In our organization, we have a centralized static code generation tool called GaaS (Git as a Service). A LiveView front-end plus a DSL for easy templates equals a very nice user experience for generating new code. GitHub templates are a free and ready-to-use alternative. mix_generator is another good tool for Elixir projects in specific.

We used static code generation tools alone for a long time with great effect.

Where static code generation tools fall short

So what's the big deal then if static code generation tools work so well? The problem lies in how your organization adopts and alters practices over time.

Consider this scenario: in an app, you add a new way of doing something that may be useful for all apps, like using the (shameless plug) bless library for running the gambit on your testing/linting suite. It's a great practice, so let's add it to all our services! To do so, we first modify our mix.exs generator(s) to depend on and/or configure bless. Then we can either run the updated generator(s) over existing codebases or manually change those services. With 10 or so services either way is fine, but over 20 or 50 or 100 services? Even small changes start to hurt.

Enter Harness

Harness takes a different approach. Harness is more like a package manager (think the mix+hex relationship or npm/yarn or cargo crates). Imagine if you checked in all the node_modules/ stuff from npm? Gross. Like red wine in a white carpet. Finding any specific file in that mess? Impossible. Changing that directory across hundreds of repositories? Herculean.

Instead, the harness approach is to describe everything a static code generation tool does with a manifest, and then never check in the generated files.

Manifests

Manifests describe a plan. They don't detail the specifics like which versions of dependencies we'll use exactly (think a mix.lock) but rather a set of rules or guidelines to use to form a project.

For harness, this is the harness.exs.

Enough of the theory though, let's Get Started!