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 applicationMOBILE_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.