DeveloperExperience

Platform engineering can become a very expensive help desk

We built an internal developer platform so developers would stop opening tickets. They now open tickets about the platform.

This is not an unusual outcome. It is merely an awkward one, especially after the organisation has spent eighteen months, several consulting invoices, and enough Kubernetes expertise to operate a small space programme.

Platform engineering is usually introduced as the answer to slow delivery, fragmented tooling, and the cognitive burden of modern cloud infrastructure. The promise is sensible. Give development teams a supported way to create services, provision environments, deploy software, and observe production without requiring every engineer to become a part-time network specialist, IAM archaeologist, and Terraform state therapist.

Then reality arrives.

The company launches a polished portal. It contains a service catalogue, a collection of templates, and a reassuring amount of corporate branding. A developer selects “Create database”, completes a form, and clicks a large blue button.

Somewhere behind that button, a Jira ticket is born.

Three days later, a platform engineer asks which subnet the database should use.

The interface changed. The bottleneck survived.

The portal is not the platform

An internal developer portal can be useful. It can bring documentation, service ownership, software templates, pipeline links, and operational information into one place. In a sufficiently large organisation, simply discovering which team owns a service can feel like a meaningful product feature.

But a portal and a platform are not the same thing.

A portal helps developers find the door. A platform should let them walk through it.

A genuine internal developer platform provides reliable, reusable capabilities that teams can consume with minimal negotiation. Those capabilities may include creating a service, provisioning a development environment, obtaining a workload identity, exposing an endpoint, attaching a managed database, or promoting a release. The important part is not where the button lives. The important part is what happens after somebody presses it.

If the result depends on another team reading the request, interpreting it, arranging a meeting, and manually running the same commands they ran last week, the organisation has not created self-service. It has created a more attractive reception desk.

Reception desks have value. They can organise demand and stop people wandering into the server room. They should not be confused with an operating model.

The help desk with better branding

The modern version of the old infrastructure request process often looks like this:

  1. A developer opens the internal portal.
  2. They choose a service template.
  3. They complete a form.
  4. The form creates a ticket.
  5. The platform team reviews the ticket.
  6. Several meetings take place because the form did not capture the important part.
  7. Somebody manually provisions the resources.

This workflow may include Kubernetes, Terraform, GitOps, and an icon set with rounded corners. None of those things changes the fact that a human queue remains the API.

The platform team gradually becomes a central operations team with more YAML and a more ambitious job title. Its engineers spend their days processing access requests, repairing template failures, explaining undocumented conventions, and performing routine changes on behalf of other teams. Because the platform is now the approved route to production, every delivery dependency eventually lands in their queue.

This arrangement is expensive in two directions. Developers wait, so product delivery slows down. Platform engineers handle repetitive work, so the platform itself barely improves. Demand increases, headcount follows, and the organisation concludes that platform engineering requires a surprisingly large team.

Sometimes it does. Sometimes the company has simply built the world’s most technically sophisticated help desk.

Golden paths and golden cages

Golden paths are one of the better ideas in platform engineering. A golden path is a recommended, well-supported route through a common delivery scenario. It should make the safe option the easy option, provide sensible defaults, and remove decisions that most teams should not need to make repeatedly.

For example, a team creating an ordinary internal API probably does not need a week-long debate about repository layout, health endpoints, log format, workload identity, or basic deployment policy. A good platform can make those choices once, encode them well, and allow the team to concentrate on the application.

The trouble begins when the path acquires walls.

A golden path becomes a golden cage when every workload must use the same runtime, every repository must have the same structure, and every exception requires a visit to an architecture board. Requirements that the platform does not support are reclassified as developer mistakes. Application teams learn that “opinionated” means the platform team has opinions and everyone else has forms.

Standards matter. Security, reliability, and operational consistency are not optional decorations. But uniformity is not the same as standardisation. A batch workload, a latency-sensitive API, and a data science environment may share governance principles without pretending to be the same system.

The path should guide teams, not trap them. Mature platforms provide documented escape hatches with clear responsibilities and controls. If too many teams take the escape hatch, that is not automatically evidence of widespread indiscipline. It may be evidence that the road goes to the wrong place.

Self-service that still requires permission

The phrase self-service has suffered badly from corporate enthusiasm.

A capability is not meaningfully self-service if a developer must request access before every action, wait for a manual approval, contact an administrator for routine configuration, or summon the platform team whenever the automation produces an error message.

Approvals are sometimes necessary. Deleting a production database should involve more friction than creating a temporary development namespace. Giving both actions the same governance process is not security maturity. It is the administrative equivalent of making everyone pass through airport immigration to visit the office kitchen.

Good platforms apply controls according to risk. Low-risk, repeatable actions should be automated and auditable. Higher-risk actions may require separation of duties, policy checks, or explicit approval. The controls should be designed into the capability instead of being added as a human checkpoint after the automation has finished pretending to be autonomous.

The goal is not to eliminate governance. It is to stop using human availability as the primary enforcement mechanism.

Abstraction is useful until it hides the wrong things

Cloud platforms exist partly to reduce cognitive load. Developers should not need to understand every detail of network routing, certificate automation, Kubernetes controllers, cloud account topology, Terraform state management, and IAM implementation before deploying an application.

This is a reasonable abstraction boundary. Nobody becomes a better product engineer by memorising the organisation’s subnet naming convention.

But abstraction can become concealment.

Developers still need to know what resources were created, where their logs live, how the service scales, what it costs, and why a deployment failed. They need enough visibility to diagnose ordinary problems without opening a support case. They also need to know who owns the next layer when the problem is not ordinary.

A good abstraction hides unnecessary mechanisms while preserving useful feedback. A bad abstraction takes a precise cloud error, removes all context, and returns PLATFORM_REQUEST_FAILED. This is technically simpler in the same way that replacing an aircraft dashboard with a single red light is technically simpler.

Platform APIs should expose status, events, ownership, and actionable errors. The underlying system can remain complex. Its behaviour should not remain mysterious.

Backstage is not a platform strategy

Backstage is a useful project. It can improve service discovery, documentation, ownership, software templates, plugin integration, and developer navigation. In many organisations, it provides an effective front door to the engineering estate.

Installing it does not create a platform organisation.

Backstage does not automatically repair slow approval processes, unreliable automation, poorly designed APIs, or organisational silos. It does not appoint a product owner, interview developers or decide which recurring problems deserve to be solved. It cannot make an infrastructure workflow self-service when every action behind the interface still depends on a person.

An organisation can deploy Backstage and continue requiring tickets for everything behind it. In that case, the portal is doing its job. The operating model is not.

The tool is not the strategy, just as buying a very good oven does not establish a restaurant. Somebody still needs to decide what the kitchen is for.

When the platform team builds for itself

Platform teams are full of infrastructure engineers, and infrastructure engineers enjoy infrastructure. This is both useful and dangerous.

The team may celebrate a sophisticated Kubernetes architecture, reusable Terraform modules, policy enforcement, multi-region control planes, elegant GitOps reconciliation, and a plugin ecosystem large enough to require its own governance committee.

Developers may still experience slow environments, confusing documentation, long onboarding, limited debugging access and no obvious owner when something fails.

Both views can be true. The architecture can be excellent while the product is poor.

This usually happens when the team optimises for technical completeness rather than developer outcomes. Developers are treated as consumers of infrastructure instead of customers of a product. Requirements arrive as requests to fulfil, not problems to understand. The roadmap becomes a list of technologies that the platform team would like to operate.

Product thinking does not mean developers are always right. A platform team must balance autonomy with security, compliance, cost, reliability, and organisational standards. It should not accept every request or support every possible runtime.

It should, however, look for patterns.

If ten teams request the same change, the answer is probably not eleven tickets. The answer may be a new capability. Tickets should become product discovery input, not the permanent delivery mechanism.

The metrics that reveal the truth

Infrastructure availability matters. Deployment success rates matter. Neither tells you whether developers are losing two days each month navigating the platform.

The uncomfortable metrics tend to be closer to the user.

Lead time

Measure the time between a developer needing a capability and being able to use it. This might include creating a service, obtaining a development environment, provisioning a database, exposing an endpoint, or receiving production access.

If the Terraform runs in six minutes but the complete process takes four days, the bottleneck is not Terraform.

Adoption

Measure whether teams choose the platform when alternatives exist. Mandatory adoption proves that management can send an email. It does not prove that the product is valuable.

Look at whether teams recommend the platform, whether new services onboard faster, and whether developers use supported paths or quietly build alternatives behind a different AWS account.

Cognitive load

Count the platform-specific knowledge required to complete ordinary work. A platform has not reduced complexity if developers must learn several internal YAML schemas, custom pipeline syntax, hidden approval rules, and a naming convention apparently derived from a lost branch of medieval accounting.

The platform should remove decisions, not replace cloud complexity with company-specific complexity.

Escape rate

Track the teams that bypass the platform or request exceptions. A high escape rate may indicate missing capabilities, excessive restrictions, poor performance, weak documentation or a platform designed around the wrong workloads.

Exceptions are data. Treating every exception as misconduct is a convenient way to avoid learning from it.

Support demand

Measure tickets, messages, and meetings per platform user or service. If adoption and support demand rise at the same rate, the platform is not scaling. It is hiring.

A healthy platform allows usage to grow faster than the number of people required to support it. The exact number will vary, but the direction should not.

What a platform product team does differently

A product-oriented platform team interviews developers and observes real delivery workflows. It prioritises recurring friction, maintains a clear roadmap, publishes service expectations, and measures user outcomes. It deprecates capabilities nobody uses and treats documentation as part of the product rather than the place where unfinished automation goes to retire.

Most importantly, it designs capabilities and APIs before designing screens.

A reliable API can support a portal, a command-line tool, a pipeline, and future integrations. A portal placed over manual processes merely conceals the queue. This is why the smallest valuable platform is often not a grand catalogue. It is one complete workflow that works from beginning to end.

Consider a developer who wants to deploy a new internal API. A mature platform might allow them to:

  • Create the service from a supported template.
  • Receive a repository with tests and pipeline configuration.
  • Provision a development environment automatically.
  • Obtain a workload identity without creating long-lived credentials.
  • Expose the service through an approved ingress pattern.
  • Access logs, metrics, and traces.
  • View cost and ownership metadata.
  • Promote the service through environments using defined controls.

The developer can see what happened, diagnose common failures, and complete routine operations without waiting for the platform team.

That is a platform capability.

A form that creates eight Jira tickets is not.

Tickets are not the enemy

Tickets remain appropriate for exceptional security reviews, regulatory approvals, high-risk production operations, significant capacity commitments, and new architectural patterns the platform does not yet support.

The problem is not that tickets exist. The problem is using tickets as the default API between developers and infrastructure teams.

Routine, repeatable, and low-risk work should become automated over time. Exceptional work should remain visible precisely because it is exceptional. When every request is a ticket, organisations lose the ability to distinguish genuine judgment from administrative habit.

Warning signs that the platform has become a help desk

The diagnosis is usually visible long before anyone admits it:

  • Developers cannot complete common tasks without contacting the platform team.
  • The portal submits requests more often than it executes actions.
  • Every new workload requires a meeting.
  • Documentation explains organisational procedures more than technical capabilities.
  • Support demand grows in proportion to adoption.
  • Teams maintain unofficial workarounds.
  • Exceptions are common but rarely influence the roadmap.
  • Platform engineers spend more time processing requests than improving capabilities.
  • Success is measured by resources provisioned rather than developer time saved.
  • Only the platform team can diagnose platform failures.

One warning sign is manageable. Eight is an operating model wearing a portal costume.

Escaping the help desk model

Start with one complete workflow. Automating three common journeys from beginning to end is more valuable than partially automating thirty services and placing the missing steps in a runbook.

Then remove one human dependency at a time. Find every point where a developer waits for another team. Decide whether policy as code, better defaults, improved documentation, or a more reliable API can remove that dependency.

Classify support requests. Repeated questions are evidence of unclear feedback, missing capabilities or documentation that exists mainly to satisfy an audit. Use those requests to shape the roadmap.

Provide governed escape hatches. Advanced teams will encounter requirements that the platform does not support. Give them a documented route with explicit ownership and controls instead of forcing the workaround underground.

Measure outcomes: elapsed time, failed attempts, voluntary adoption, support demand, and developer satisfaction. Resource counts describe activity. Time saved describes value.

Finally, keep the platform smaller than its ambitions. Do not abstract every cloud service or support every application pattern. A platform becomes valuable by being dependable, not universal. The cloud providers have already built enormous catalogues. You do not need to recreate one with fewer engineers and an internal logo.

The conversations that no longer need to happen

Platform engineering is valuable when it reduces the organisational interactions required to deliver software safely. It fails when the platform team becomes another mandatory stop on the journey to production.

A portal can hide complexity. A platform should remove unnecessary dependencies.

The distinction is easy to miss because both can have templates, catalogues, APIs, and excellent diagrams. You see it in what developers do next. If routine work completes reliably, with useful feedback and appropriate controls, the platform is doing its job. If the next step is waiting for somebody to read a ticket, the organisation has only moved the queue behind a nicer door.

The success of an internal developer platform is not measured by how many services appear in its catalogue. It is measured by how many routine conversations no longer need to happen.

Docker didn’t die, it just moved to your laptop

Docker used to be the answer you gave when someone asked, “How do we ship this thing?” Now it’s more often the answer to a different question, “How do I run this thing locally without turning my laptop into a science fair project?”

That shift is not a tragedy. It’s not even a breakup. It’s more like Docker moved out of the busy downtown apartment called “production” and into a cozy suburb called “developer experience”, where the lawns are tidy, the tools are friendly, and nobody panics if you restart everything three times before lunch.

This article is about what changed, why it changed, and why Docker is still very much worth knowing, even if your production clusters rarely whisper its name anymore.

What we mean when we say Docker

One reason this topic gets messy is that “Docker” is a single word used to describe several different things, and those things have very different jobs.

  • Docker Desktop is the product that many developers actually interact with day to day, especially on macOS and Windows.
  • Docker Engine and the Docker daemon are the background machinery that runs containers on a host.
  • The Docker CLI and Dockerfile workflow are the human-friendly interface and the packaging format that people have built habits around.

When someone says “Docker is dying,” they usually mean “Docker Engine is no longer the default runtime in production platforms.” When someone says “Docker is everywhere,” they often mean “Docker Desktop and Dockerfile workflows are still the easiest way to get a containerized dev environment running quickly.”

Both statements can be true at the same time, which is annoying, because humans prefer their opinions to come in single-serving packages.

Docker’s rise and the good kind of magic

Docker didn’t become popular because it invented containers. Containers existed before Docker. Docker became popular because it made containers feel approachable.

It offered a developer experience that felt like a small miracle:

  • You could build images with a straightforward command.
  • You could run containers without a small dissertation on Linux namespaces.
  • You could push to registries and share a runnable artifact.
  • You could spin up multi-service environments with Docker Compose.

Docker took something that used to feel like “advanced systems programming” and turned it into “a thing you can demo on a Tuesday.”

If you were around for the era of XAMPP, WAMP, and “download this zip file, then pray,” Docker felt like a modern version of that, except it didn’t break as soon as you looked at it funny.

The plot twist in production

Here is the part where the story becomes less romantic.

Production infrastructure grew up.

Not emotionally, obviously. Infrastructure does not have feelings. It has outages. But it did mature in a very specific way: platforms started to standardize around container runtimes and interfaces that did not require Docker’s full bundled experience.

Docker was the friendly all-in-one kitchen appliance. Production systems wanted an industrial kitchen with separate appliances, separate controls, and fewer surprises.

Three forces accelerated the shift.

Licensing concerns changed the mood

Docker Desktop licensing changes made a lot of companies pause, not because engineers suddenly hated Docker, but because legal teams developed a new hobby.

The typical sequence went like this:

  1. Someone in finance asked, “How many Docker Desktop users do we have?”
  2. Someone in legal asked, “What exactly are we paying for?”
  3. Someone in infrastructure said, “We can probably do this with Podman or nerdctl.”

A tool can survive engineers complaining about it. Engineers complain about everything. The real danger is when procurement turns your favorite tool into a spreadsheet with a red cell.

The result was predictable: even developers who loved Docker started exploring alternatives, if only to reduce risk and friction.

The runtime world standardized without Docker

Modern container platforms increasingly rely on runtimes like containerd and interfaces like the Container Runtime Interface (CRI).

Kubernetes is a key example. Kubernetes removed the direct Docker integration path that many people depended on in earlier years, and the ecosystem moved toward CRI-native runtimes. The point was not to “ban Docker.” The point was to standardize around an interface designed specifically for orchestrators.

This is a subtle but important difference.

  • Docker is a complete experience, build, run, network, UX, opinions included.
  • Orchestrators prefer modular components, and they want to speak to a runtime through a stable interface.

The practical effect is what most teams feel today:

  • In many Kubernetes environments, the runtime is containerd, not Docker Engine.
  • Managed platforms such as ECS Fargate and other orchestrated services often run containers without involving Docker at all.

Docker, the daemon, became optional.

Security teams like control, and they do not like surprises

Security teams do not wake up in the morning and ask, “How can I ruin a developer’s day?” They wake up and ask, “How can I make sure the host does not become a piñata full of root access?”

Docker can be perfectly secure when used well. The problem is that it can also be spectacularly insecure when used casually.

Two recurring issues show up in real organizations:

  • The Docker socket is powerful. Expose it carelessly, and you are effectively offering a fast lane to host-level control.
  • The classic pattern of “just give developers sudo docker” can become a horror story with a polite ticket number.

Tools and workflows that separate concerns tend to make security people calmer.

  • Build tools such as BuildKit and buildah isolate image creation.
  • Rootless approaches, where feasible, reduce blast radius.
  • Runtime components can be locked down and audited more granularly.

This is not about blaming Docker. It’s about organizations preferring a setup where the sharp knives are stored in a drawer, not taped to the ceiling.

What Docker is now

Docker’s new role is less “the thing that runs production” and more “the thing that makes local development less painful.”

And that role is huge.

Docker still shines in areas where convenience matters most:

  • Local development environments
  • Quick reproducible demos
  • Multi-service stacks on a laptop
  • Cross-platform consistency on macOS, Windows, and Linux
  • Teams that need a simple standard for “how do I run this?”

If your job is to onboard new engineers quickly, Docker is still one of the best ways to avoid the dreaded onboarding ritual where a senior engineer says, “It works on my machine,” and the junior engineer quietly wonders if their machine has offended someone.

A small example that still earns its keep

Here is a minimal Docker Compose stack that demonstrates why Docker remains lovable for local development.

services:
  api:
    build: .
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: postgres://postgres:example@db:5432/app
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
      POSTGRES_DB: app
    ports:
      - "5432:5432"

This is not sophisticated. That is the point. It is the “plug it in and it works” power that made Docker famous.

Dockerfile is not the Docker daemon

This is where the confusion often peaks.

A Dockerfile is a packaging recipe. It is widely used. It remains a de facto standard, even when the runtime or build system is not Docker.

Many teams still write Dockerfiles, but build them using tooling that does not rely on the Docker daemon on the CI runner.

Here is a BuildKit example that builds and pushes an image without treating the Docker daemon as a requirement.

buildctl build \
  --frontend dockerfile.v0 \
  --local context=. \
  --local dockerfile=. \
  --output type=image,name=registry.example.com/app:latest,push=true

You can read this as “Dockerfile lives on, but Docker-as-a-daemon is no longer the main character.”

This separation matters because it changes how you design CI.

  • You can build images in environments where running a privileged Docker daemon is undesirable.
  • You can use builders that integrate better with Kubernetes or cloud-native pipelines.
  • You can reduce the amount of host-level power you hand out just to produce an artifact.

What replaced Docker in production pipelines

When teams say they are moving away from Docker in production, they rarely mean “we stopped using containers.” They mean the tooling around building and running containers is shifting.

Common patterns include:

  • containerd as the runtime in Kubernetes and other orchestrated environments
  • BuildKit for efficient builds and caching
  • kaniko for building images inside Kubernetes without a Docker daemon
  • ko for building and publishing Go applications as images without a Dockerfile
  • Buildpacks or Nixpacks for turning source code into runnable images using standardized build logic
  • Dagger and similar tools for defining CI pipelines that treat builds as portable graphs of steps

You do not need to use all of these. You just need to understand the trend.

Production platforms want:

  • Standard interfaces
  • Smaller, auditable components
  • Reduced privilege
  • Reproducible builds

Docker can participate in that world, but it no longer owns the whole stage.

A Kubernetes-friendly image build example

If you want a concrete example of the “no Docker daemon” approach, kaniko is a popular choice in cluster-native pipelines.

apiVersion: batch/v1
kind: Job
metadata:
  name: build-image-kaniko
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: kaniko
          image: gcr.io/kaniko-project/executor:latest
          args:
            - "--dockerfile=Dockerfile"
            - "--context=dir:///workspace"
            - "--destination=registry.example.com/app:latest"
          volumeMounts:
            - name: workspace
              mountPath: /workspace
      volumes:
        - name: workspace
          emptyDir: {}

This is intentionally simplified. In a real setup, you would bring your own workspace, your own auth mechanism, and your own caching strategy. But even in this small example, the idea is visible: build the image where it makes sense, without turning every CI runner into a tiny Docker host.

The practical takeaway for architects and platform teams

If you are designing platforms, the question is not “Should we ban Docker?” The question is “Where does Docker add value, and where does it create unnecessary coupling?”

A simple mental model helps.

  • Developer laptops benefit from a friendly tool that makes local environments predictable.
  • CI systems benefit from builder choices that reduce privilege and improve caching.
  • Production runtimes benefit from standardized interfaces and minimal moving parts.

Docker tends to dominate the first category, participates in the second, and is increasingly optional in the third.

If your team still uses Docker Engine on production hosts, that is not automatically wrong. It might be perfectly fine. The important thing is that you are doing it intentionally, not because “that’s how we’ve always done it.”

Why this is actually a success story

There is a temptation in tech to treat every shift as a funeral.

But Docker moving toward local development is not a collapse. It is a sign that the ecosystem absorbed Docker’s best ideas and made them normal.

The standardization of OCI images, the popularity of Dockerfile workflows, and the expectations around reproducible environments, all of that is Docker’s legacy living in the walls.

Docker is still the tool you reach for when you want to:

  • start fast
  • teach someone new
  • run a realistic stack on a laptop
  • avoid spending your afternoon installing the same dependencies in three different ways

That is not “less important.” That is foundational.

If anything, Docker’s new role resembles a very specific kind of modern utility.

It is like Visual Studio Code.

Everyone uses it. Everyone argues about it. It is not what you deploy to production, but it is the thing that makes building and testing your work feel sane.

Docker didn’t die.

It just moved to your laptop, brought snacks, and quietly let production run the serious machinery without demanding to be invited to every meeting.