Production engineering guide for platform maturity assessment covering patterns, implementation strategies, and operational best practices.
Platform Maturity Assessment evaluates the evolution and capability of a platform engineering function within an organisation. It identifies gaps, tracks progress, and guides investment by measuring how effectively platform services support developer productivity, system reliability, and operational efficiency. Use this assessment when onboarding a new team, before major platform rearchitecture, after a significant incident, or as a recurring audit to align platform strategy with developer needs.
The assessment is not a one-off survey but a hands-on exploration of platform artifacts, workflows, and developer interactions. Start by defining the scope: which teams, services, and platforms are in scope? Then gather evidence through a series of targeted checks, each revealing a dimension of maturity.
Begin by identifying all platform components in use. The platform is only as good as its discoverability. A mature platform is self-documenting and easily navigable.
kubectl get services -n platform and examine spec.selector and spec.type fields. A platform is mature when services use consistent labels: app.kubernetes.io/name, app.kubernetes.io/part-of, app.kubernetes.io/managed-by.platform-service CRD in Kubernetes. Check that every service declares: apiVersion: platform.example/v1
kind: PlatformService
metadata:
name: auth-service
namespace: platform
spec:
displayName: "Authentication Service"
description: "User login, token management, and SSO."
owner: "[email protected]"
documentation: "https://docs.example.com/platform/auth"
tags:
- "identity"
- "security"
- "api"
PlatformService CRD is consumed by the developer portal. A simple curl -H "Accept: application/json" https://portal.example.com/api/services | jq should return a list with id, name, description, and documentation fields.Silent failure: PlatformService resources exist but are not synced to the portal. The portal queries a database, and platform_service entries are not updated after kubectl apply -f auth-service.yaml. The root cause is a missing watch in the sync controller. A kubectl logs -l app=platform-portal-sync reveals:
failed to update service auth-service: failed to fetch documentation from https://docs.example.com/platform/auth: 404 Not Found
This indicates the documentation URL is incorrect or the service is not deployed.
A mature platform empowers developers to onboard and deploy services with minimal platform team intervention.
git clone to kubectl apply -f k8s/ to a live service. Check that every team has a platform-onboarding Git repository with:docs/onboarding.md describing the processtemplates/ directory with kustomize/, helm/, and terraform/ templatesexamples/ with a fully working servicecd examples/auth && make deploy and confirming:kubectl get deployments -n productionkubectl get services -n production -l app=auth-servicecurl -H "Accept: application/json" http://auth-service.example.com/health returns {"status": "UP"}The critical check: make deploy must be idempotent and predictable. Run it twice and observe no changes.
Failure mode: make deploy fails the second time. Inspection shows:
$ make deploy
INFO: deploying auth-service to production
INFO: creating namespace production
INFO: applying kustomization
Error: failed to apply kustomization: exit status 1
stderr:
Error: failed to apply manifests: failed to create service: Service "auth-service" already exists
The root cause is that kustomize build k8s/ does not include name in Service manifest, so kubectl apply treats auth-service as a new resource even when it exists.
Fix: ensure kustomization.yaml includes:
resources:
- base/
patchesStrategicMerge:
- service.yaml
namePrefix: auth-service-
Now make deploy is idempotent.
Assess how well platform standards are enforced through golden paths.
git push origin main triggers CI, builds, and deploys.ci.yaml in the root of the repository. It must define:jobs: with build, test, deploysteps: that are reusable and versionedneeds: to orchestrate dependency between jobsExample ci.yaml:
name: Deploy Service
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker Image
run: docker build -t ghcr.io/example/auth-service:$GITHUB_SHA .
- name: Push Image
run: |
docker push ghcr.io/example/auth-service:$GITHUB_SHA
echo "IMAGE=$GITHUB_SHA" >> $GITHUB_ENV
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to Production
run: |
kubectl set image deployment/auth-service \
auth-service=ghcr.io/example/auth-service:$IMAGE \
--namespace=production
The platform is mature when ci.yaml is a standard across all services, and new teams copy it with minimal changes.
Failure mode: deploy job fails due to missing IMAGE environment variable. The deploy job runs kubectl set image, but IMAGE is not set. The deploy job logs show:
Error: failed to apply deployment: error: no such file or directory
The root cause is that echo "IMAGE=$IMAGE" >> $GITHUB_ENV runs in a shell that does not inherit GITHUB_ENV from build. The fix is to use env: in the deploy job:
deploy:
needs: build
runs-on: ubuntu-latest
env:
IMAGE: ${{ secrets.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Deploy to Production
run: |
kubectl set image deployment/auth-service \
auth-service=ghcr.io/example/auth-service:$IMAGE \
--namespace=production
A mature platform provides immediate visibility into service health and enables rapid incident response.
prometheus metrics at /metrics. Check with: curl -s http://auth-service.example.com/metrics | grep "http_request_duration_seconds"
curl -s 'http://prometheus.example.com/api/v1/query?query=http_request_duration_seconds_count{job="auth-service"}'
health endpoint at /health returning: {
"status": "UP",
"details": {
"database": {
"status": "UP",
"details": {
"connection": "OK",
"latency": 12.4
}
},
"cache": {
"status": "UP",
"details": {
"ping": "OK",
"hits": 98.2
}
}
}
}
journalctl -u auth-service outputs structured JSON logs: {
"timestamp": "2024-04-15T12:34:56Z",
"level": "info",
"message": "User login successful",
"user_id": 12345,
"duration_ms": 42
}
fluentd or vector and indexed in a search system like Elasticsearch.Silent failure: health endpoint returns {"status": "UP"} but details are missing. The platform defines a HealthCheck interface, but services implement it inconsistently. One service returns details as a map[string]string, another as []string.
The fix: standardize the HealthCheck contract in a common-health Go module. Services must export HealthCheck struct with:
type HealthCheck struct {
Status string `json:"status"`
Details map[string]Detail `json:"details"`
}
type Detail struct {
Status string `json:"status"`
Details map[string]any `json:"details"`
}
Now the central dashboard consumes health responses uniformly.
A mature platform includes mechanisms for feedback, governance, and evolution.
platform-issues label in the repository. Create a new issue with: curl -X POST \
-H "Content-Type: application/json" \
-d '{
"title": "Platform feature request: auto-scaling for auth-service",
"body": "We need auto-scaling based on JWT token load.",
"labels": ["platform", "feature"]
}' \
https://api.github.com/repos/example/auth-service/issues
curl -H "Accept: application/json" \
https://api.github.com/repos/example/platform/issues?labels=platform,review
platform-reviews process: every service must receive a review before production deployment.reviews/ directory in the platform repository. Each review is a review-<date>.md file with:reviewer:, service:, date:findings:, recommendations:, action-items:Failure mode: reviews are not linked to issues. A review-2024-04-01.md is created, but the GitHub issue has no link. The root cause is that the platform-review tool expects a review-id field in the YAML header:
---
reviewer: [email protected]
service: auth-service
date: 2024-04-01
review-id: 1234
---
Without review-id, the tool does not update the issue.
After gathering evidence, score the platform using a rubric of five levels: Novice, Apprentice, Journeyman, Expert, Master.
Each level is defined by the presence and consistency of practices:
The assessment is complete when all evidence points to a single level. The next step is to define actions to move to the next level.
This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy