Skip to main content

Application taxonomy — kind, interfaces, tech

An application is classified on three independent axes. Keeping them separate is what lets the platform reason about what to test, how to test it, and which quality bar applies.

The three axes

1. Kind — what it is (single value)

  • SERVICE — a deployable backend unit (includes workers, daemons and batch jobs)
  • WEB_APP — a browser-delivered application
  • MOBILE_APP — an installable mobile application

Network primitives (IP, CIDR, hostname) are deliberately not application kinds — they are addressing, and exist only as environment targets.

2. Interfaces — what it exposes (multi-valued)

REST · GRAPHQL · GRPC · SOAP · WEBSOCKET · MESSAGING · DATABASE · WEB_UI · MOBILE_UI · BATCH

This is the axis that was missing. A real service speaks several protocols at once:

{
"name": "orders-service",
"applicationKind": "SERVICE",
"applicationInterfaces": ["REST", "MESSAGING", "DATABASE"],
"applicationTech": "SPRING_BOOT"
}

That is one application, not three. A test targets an application and one of its interfaces ("load-test orders-service's MESSAGING interface"), and release readiness rolls up across a service's interfaces — "is orders-service ready to ship?" aggregates its REST + messaging + database

  • performance + security results.

The broker or engine is not the axis: Kafka, RabbitMQ and SQS are all MESSAGING. The concrete broker lives on the environment target that binds that interface in a given environment.

3. Tech — what it is built with (single value)

SPRING_BOOT, DJANGO, REACT, NODE_JS, … — the framework/language. It drives scoring milestones and engine recommendations (a React app gets a Playwright crawl recommendation). Tech is never a protocol.

Reading the vocabulary

Do not hardcode these lists — fetch them:

GET /api/applications/taxonomy
{ "kinds": [...], "interfaces": [{value, label, impliedKind}], "techs": [...] }

Compatibility: applicationType

The older single field applicationType is kept and dual-written from applicationInterfaces (the primary interface maps back to a legacy type — REST → REST_SERVICE, MESSAGING → QUEUE, WEB_UI → WEB_APP, …). Existing filters and integrations keep working unchanged. New code should read applicationKind + applicationInterfaces.

If an application exposes a UI and an API, the UI interface wins the compatibility mapping — so a service with an admin console stays visible to UI-test and crawl flows.

Backfill

Existing applications were classified deterministically from their old type: REST_SERVICE → SERVICE/[REST], WEB_APP → WEB_APP/[WEB_UI], QUEUE → SERVICE/[MESSAGING], and so on. Rows whose old type was an addressing kind (NETWORK/IP/hostname), or that had no type and no API spec, are left unclassified rather than guessed — they surface for review instead of being silently mislabeled.