A self-service platform becomes brittle when application repositories contain the platform team's implementation language. Developers who only need a service endpoint, a database, and a queue end up maintaining Kubernetes objects, provider resources, environment overlays, and orchestration details. The Score workload specification offers a narrower contract: a workload declares what it needs to run, while a Score implementation translates that intent for a target platform.
That separation is valuable, but the file format alone is not a platform API. A useful platform workload contract also defines supported resource types, output semantics, validation, policy, ownership, environment binding, status, and change compatibility. Score is intentionally scoped to workload-level properties and is both platform-agnostic and environment-agnostic. Platform teams must preserve that boundary rather than leaking every infrastructure option back through annotations and custom extensions.
Set the workload contract boundary

Start with responsibilities. Application teams own the workload name, container image, command, runtime variables, service ports, and declarations of dependencies. The platform owns how environments resolve those dependencies, which infrastructure classes are available, network placement, credentials delivery, policy, observability injection, and lifecycle operations. Security and operations requirements can be shared, but one side must be accountable for each field. If both sides edit the same generated manifest, the contract has already failed.
The current Score reference describes one workload with required workload metadata and containers, plus optional service and resources sections. Resource outputs can be interpolated into container variables, files, or other inputs. This supports an intent such as a dependency named orders-db of a platform-defined type without putting a cloud database SKU in the application file. The target environment decides whether that type maps to a local container, a shared test service, or a managed production database.
| Concern | Developer contract | Platform implementation | Do not expose by default |
|---|---|---|---|
| Compute | Image, command, ports, workload variables | Scheduler objects, identity, placement, runtime defaults | Node pools and provider-specific instance details |
| Dependency | Logical resource type and required outputs | Provisioner, class, network, backup, credentials | Raw module variables or cloud API shape |
| Environment | Portable declarations and references | Policy-selected parameters and bindings | Copied environment manifests in the app repo |
| Status | Conditions meaningful to the workload owner | Controller and provider diagnostics | Unfiltered backend error dumps |
| Lifecycle | Create, update, observe, and retire intent | Reconciliation, state, deletion controls | Implementation-specific state mutation |
Design resource types as products
A resource type is more than a convenient label. Define its purpose, required parameters, optional parameters, outputs, lifecycle, service levels, data classification limits, regions, cost attribution, deletion behavior, and support owner. Prefer capability names such as postgres, object-store, or dns only when their semantics are genuinely stable across implementations. If two offerings have materially different consistency, recovery, tenancy, or operational behavior, model separate classes or an explicit service tier instead of hiding the difference.
Score-k8s resource provisioners demonstrate the implementation point: platform engineers provide concrete implementations for resource types used in a workload's resources section. Provisioners may generate resources and return outputs consumed by the workload. Treat their configuration as privileged platform code. Validate parameters, constrain templates, protect credentials, test deletion, and version the output contract. A developer should not gain arbitrary command execution or unrestricted infrastructure attributes through what appears to be a harmless resource declaration.
Validate intent before translation
Run schema validation in the developer loop and again at the trusted platform boundary. Check the declared API version, required metadata, container structure, interpolation references, duplicate ports, resource type availability, parameter schemas, and organization policy. Return errors against the developer's vocabulary: name the resource, invalid field, accepted values, and remediation. A translator error that exposes a failed Helm expression or provider attribute forces users to understand an implementation the contract was meant to hide.
Separate syntactic validity from admission. A specification can satisfy the Score schema while requesting a resource class unavailable in a region, violating a data policy, exceeding a team quota, or lacking ownership. Produce a normalized intermediate request, evaluate environment capability and policy, then resolve it. Preserve the original request, normalized form, policy decision, implementation version, and resulting object identities so support staff can reproduce what happened.
| Validation layer | Question | Failure response | Evidence |
|---|---|---|---|
| Schema | Is the document structurally valid for the API version? | Fast local error | Validator and schema version |
| Reference | Do variables and resource outputs resolve? | Field-level error | Normalized dependency graph |
| Capability | Does the target environment offer this type and tier? | Alternatives or unsupported result | Capability catalog revision |
| Policy | May this owner request it in this context? | Explainable deny, warn, or approval | Decision ID and policy revision |
| Realization | Did generated resources become ready? | Stable condition plus diagnostic link | Implementation revision and resource IDs |
Bind environments outside the workload file
Environment agnosticism means the application declaration contains properties that should remain constant as it moves. Environment-specific values still exist; they belong in a controlled binding layer. Resolve resource class, account, region, domain, identity, quotas, and secret references from the deployment target and policy. Avoid a growing set of if production annotations in score.yaml, because those recreate overlays inside the supposedly portable contract.
Test portability with conformance cases, not slogans. Render the same workload for local and production implementations and assert stable workload semantics: required ports, variable names, resource outputs, health behavior, and identity expectations. The generated objects will differ, and certain capabilities may be unavailable. Report those differences before deployment. Portability is the ability to preserve declared intent or receive an explicit incompatibility, not a promise that every runtime has identical features.
Keep secrets and authority out of the declaration
A workload may reference a resource output such as a connection string, but it should not contain the resulting credential. Score guidance places responsibility on implementations to handle sensitive outputs appropriately. Use workload identity where possible, deliver secrets through the target platform's protected mechanism, avoid placing values in generated files or logs, and ensure previews redact them. Define whether an output is public configuration, sensitive configuration, or a secret so implementations cannot guess.
The translator's authority should be narrower than the entire platform. Separate validation from realization, scope provisioner identities by resource family and environment, and require explicit permission for destructive operations. Sign or otherwise integrity-protect implementation packages and provisioner configuration. Record who requested a change and what approved it. Because translation can create privileged runtime objects, reviewing a workload file is not a substitute for controlling the implementation supply chain.
Version the contract and expose stable status
Treat the Score API version, organizational resource taxonomy, and each implementation release as separate versions. A schema-compatible change may still alter resource behavior. Publish deprecations with discovery of affected workloads, a replacement, an overlap period, and migration tests. Keep old implementations available long enough to roll forward safely, and pin production translation to a reviewed release rather than silently using the newest provisioner configuration.
Return a small set of conditions that remain meaningful across backends: accepted, progressing, ready, degraded, and deletion-blocked, each with reason, observed generation, and timestamp. Link to implementation diagnostics without making backend status the API. Ensure retries are idempotent and reconcile partially created resources. If an implementation is only a manifest generator and does not manage lifecycle, say so clearly; the Score project does not require every implementation to provide complex provisioning or deployment operations.
Introduce the contract through one thin use case
Choose a common workload with two or three dependencies and painful environment duplication. Define only the fields needed for that case, build local and production mappings, and onboard volunteer teams. Compare time to first successful deployment, invalid-request rate, support causes, policy failures, and manual escape hatches. Expand the taxonomy only after repeated demand. A giant schema designed before observing users becomes another infrastructure language and transfers platform complexity into application repositories.
- Inventory developer-owned intent and platform-owned decisions for the pilot.
- Define resource types, parameters, outputs, ownership, and deletion semantics.
- Build schema, capability, policy, and realization validation with actionable errors.
- Implement two environments and run shared contract conformance tests.
- Expose stable conditions, decision evidence, and backend diagnostics.
- Measure adoption and exceptions before adding more fields or resource classes.
Key takeaways
- Use Score to express workload intent, not to mirror the target platform.
- Operate resource types as versioned platform products with output and lifecycle contracts.
- Bind environment choices and policy outside the portable workload file.
- Protect secret outputs and narrow the authority of provisioners.
- Test semantic conformance across implementations and report incompatibility explicitly.
Frequently asked questions
Does Score replace Kubernetes or infrastructure as code?
No. It provides a workload-level input that an implementation translates. Kubernetes, infrastructure modules, controllers, or other systems may still realize and operate the request. The benefit is keeping those details behind a platform-owned mapping rather than requiring every developer to author them.
Can every workload use one Score file unchanged?
Only where its declared capabilities exist. The contract should preserve environment-independent intent and produce a clear capability error when a target cannot satisfy it. Specialized workloads may need another interface rather than an expanding set of provider-specific annotations.
Who owns the resource provisioners?
The platform capability owner should own implementation, testing, security, service behavior, output compatibility, and support. Application teams own their request and correct consumption of documented outputs. Shared accountability should not become ambiguous edit rights over the same generated infrastructure.
Conclusion
Score can form a clean workload contract when the platform resists turning it into another vendor configuration language. Keep developer intent small, make resource semantics explicit, bind environments through controlled implementations, and preserve evidence from request to runtime. The resulting interface reduces cognitive load because it moves real decisions to accountable platform owners, while still giving developers portable, testable control over what their workloads require.