Jump to content

Service Scaffolding

From mediawiki.org

Background[edit]

We’ve had great success in the past using the service-template-node project to facilitate creation of NodeJS-based services that follow established best practices and conventions, but improvements are possible.

One pain point is that the template repository serves as both a starting point for new projects, as well as a source of common code.  This necessitates that as bugs are found and fixed, or improvements are made, changes from the template be merged into service repositories.  This requires service maintainers to monitor for changes, and deal with the fallout of inevitable merge conflicts.  To solve for this, common (reusable) code should be placed in shared libraries and imported into projects.  Identifying and incorporating updates of these shared libraries can then be carried out in the usual, idiomatic way.

With all reusable code in libraries, what remains is boilerplate to facilitate getting started, and document expected layout and conventions.  There is no expectation that changes here will ever need to be merged forward.  This approach is being referred to as scaffolding to differentiate it from the previous approach.  Why scaffolding?  From Wikipedia:

Complicated software projects often share certain conventions on project structure and requirements. For example, they often have separate folders for source code, binaries and code tests, as well as files containing license agreements, release notes and contact information. To simplify the creation of projects following those conventions, "scaffolding" tools can automatically generate them at the beginning of each project. Such tools include Yeoman, Cargo and Ritchie CLI.

Though we’re not initializing a new tree by generating the files, the approach outlined here is conceptually identical.

$ git clone https://.../service-scaffold-{lang}
$ rm -r .git
$ git init
$ # Edit as necessary
$ git commit -m ‘Initial commit’
Fig. 1: Expected use

Scaffold projects[edit]

  • Must be named in the form service-scaffold-{lang}
  • Must include a Makefile, even if it only wraps invocation of another build system
    • Default (first) target should execute any steps necessary to ready local execution
      • Should be named build
      • Think npm install for Node projects, or go build for Go
    • test (wrapper)
      • Depends on check (see below)
      • Depends on unit-test (see below)
      • Should not require pre-configuration of external services or dependencies
    • check target that runs any linters, checkers, formatters, etc
      • Should not require pre-configuration of external services or dependencies
    • unit-test target that runs unit tests
      • Should not require pre-configuration of external services or dependencies
    • clean target that removes generated detritus (at least anything not .gitignore-d)
    • [ … ]
  • Must demonstrate importing of the corresponding servicelib-{lang}
  • Must be “working” (helloworld, echo, etc)
    • With tests
    • CI configured, building a Docker image, etc
    • Demonstrate structured logging (for k8s)
    • Demonstrate /healthz (readiness endpoint)
    • Demonstrate Prometheus instrumentation
  • Should include a placeholder for licensing
    • Though choice of terms is not mandated by the scaffold
    • An otherwise blank file containing guidance/pointers on license selection?
    • Ship a default for people who don’t know how to choose (or don’t care?)
  • Should strive for minimalism (minimum effective dose)
    • Not the place for your kitchen sink

Service libraries[edit]

  • Must be named in the form servicelib-{lang}
  • Should provide an abstraction for creating /healthz endpoints
    • Demonstrates service readiness (only return 200 when ready to answer requests)
    • Returns a JSON object conforming to specific schema (see below)
  • Should include a placeholder for licensing
  • Should provide an example of structured logging
  • May provide abstractions for Prometheus instrumentation
    • If doing so would make doing the Right Thing™ easier
    • If a one-size-fits-all solution is possible

Healthz[edit]

{
  "version": "1.0.0-0b56c7d",
  "build_date": "2021-08-20T13:19:51-05:00",
  "build_host": "hermes"
}
Fig. 2: Example JSON output

Implementations[edit]

NodeJS (WIP)[edit]

https://github.com/nikkhn/service-scaffold-node

https://github.com/nikkhn/servicelib-node

Go (WIP)[edit]

https://github.com/nikkhn/service-scaffold-golang

https://github.com/eevans/servicelib-golang/

References[edit]