What Platform Engineers Actually Build, A Breakdown of a Real Internal Developer Platform | PulseSoft
PulseSoft

What Platform Engineers Actually Build, A Breakdown of a Real Internal Developer Platform

PulseSoft Team · September 24, 2026 · 8 min read

Introduction

Every job description I've reviewed for a "Platform Engineer" role describes the outcome ("enable developer self-service," "improve deployment velocity"), and almost none of them describe what the person is actually supposed to build. I've hired for this role three times and rewritten the job description each time, because the generic version attracts people who want to talk about culture and repels people who've actually shipped an internal developer platform.

An internal developer platform is not a product you install. It's a specific set of systems, usually five or six, that a platform team builds, owns, and operates, each solving a distinct problem for the engineers who use it. If you can't name those systems specifically, you don't have a platform, you have a set of scripts and a wiki page.

This post breaks down what those systems actually are on AWS, what each one costs to build and maintain, and where teams consistently either skip a critical piece or over-invest in one that wasn't needed yet.

The Core Problem: "Platform" Is a Vague Word for a Specific Set of Systems

The term "internal developer platform" gets used to describe everything from a single Backstage instance to a full self-service provisioning system, which makes it nearly impossible to evaluate whether a team's platform investment is proportionate to their actual needs. That ambiguity is where most platform engineering initiatives go wrong, teams start building "a platform" without agreeing on which specific problems it needs to solve first.

In practice, a real internal developer platform is composed of a small number of distinct systems, each addressing a different point of friction: a provisioning layer (how new infrastructure gets created), a deployment layer (how code gets from a commit to running in an environment), an observability layer (how engineers understand what's happening in production), a configuration and secrets layer (how services get the environment-specific values they need), and increasingly, a cost and governance layer (how the org keeps track of what all this is costing and who's allowed to spin up what).

The most common mistake I see is teams building the deployment layer first, usually because it's the most visible and the most immediately gratifying, while skipping the provisioning layer entirely, leaving infrastructure creation as a manual, ticket-based process running through the platform team. The result: developers get a beautiful CI/CD pipeline for deploying code to infrastructure that still takes two weeks and three Slack threads to provision in the first place. The bottleneck just moved, it didn't disappear.

A concrete example from a mid-sized SaaS client: they'd built an excellent Backstage-based deployment portal with one-click promotion between environments, but every new service still required a platform engineer to manually create the ECR repository, the IAM roles, and the initial VPC networking by hand through the console. Their "self-service platform" had a two-week lead time for anything genuinely new, because the provisioning layer, the actual foundation, had never been built.

AWS Deep Dive: The Systems That Make Up a Real Platform

The Provisioning Layer: Terraform Modules and Account Vending, Not Console Clicks

The provisioning layer is what turns "I need a new service with a database and a queue" into a repeatable, self-service action instead of a manual infrastructure request. On AWS, this is almost always built on top of versioned Terraform modules: not raw Terraform that developers write themselves, but pre-built, opinionated modules that encode your org's standards (encryption at rest, tagging conventions, IAM boundary policies) so a developer calling the module can't accidentally produce a non-compliant resource.

module "checkout_service" {
  source  = "git::https://github.com/pulsesoft/platform-modules.git//service?ref=v2.4.0"

  service_name       = "checkout"
  team               = "commerce"
  environment        = "production"
  database_engine    = "postgres"
  requires_queue     = true
  compute_type       = "fargate"
}

For orgs running multiple AWS accounts (which should be the default past a small handful of services, for blast-radius and billing isolation reasons), the provisioning layer also needs an account vending mechanism. AWS Control Tower or a custom Account Factory built on Organizations APIs handles this: a new team or environment gets its own account, pre-wired with baseline guardrails (Service Control Policies, centralized logging to a security account, a standard VPC layout) without a human manually clicking through account creation and baseline setup each time.

The non-obvious gotcha: Service Control Policies apply at the OU level and are deny-by-default once you start using them restrictively, which means a poorly tested SCP rolled out to a top-level OU can silently break API calls across every account beneath it (including ones unrelated to what you were trying to restrict. Always test new SCPs against a single sandboxed OU first, and know that SCPs never grant permissions, they only restrict what IAM policies within the account can otherwise allow), a common misunderstanding that leads teams to write an SCP expecting it to grant access, which it will never do.

The Configuration and Secrets Layer: Making Environment Differences Invisible to Application Code

A platform's configuration layer should mean a developer's application code never contains an environment-specific value, and never directly calls the AWS SDK to fetch a secret. On AWS, this is typically AWS Systems Manager Parameter Store or Secrets Manager as the source of truth, with an operator or sidecar (External Secrets Operator on Kubernetes, or the ECS Secrets integration on ECS), handling the injection so the application just reads an environment variable or a mounted file, with no awareness of where the value came from.

aws ssm put-parameter \
  --name "/platform/checkout/production/database-host" \
  --value "checkout-db.cluster-xyz.us-east-1.rds.amazonaws.com" \
  --type "SecureString" \
  --tags "Key=team,Value=commerce" "Key=service,Value=checkout"

The tradeoff between Parameter Store and Secrets Manager matters more than most teams realize: Secrets Manager supports automatic rotation with Lambda-backed rotation functions and costs per secret per month, while Parameter Store's SecureString tier is essentially free but has no built-in rotation mechanism. Teams building a platform layer often default to Parameter Store for everything to save cost, then have to retrofit rotation later for anything touching a real credential (database passwords, API keys for third-party services), which is a much larger project once dozens of services already depend on the unrotated pattern. Decide per-secret, not per-platform, based on whether that specific value needs rotation.

The Observability Layer: Structured by Service Ownership, Not Just Aggregated Logs

The observability layer is where platform teams most often confuse "we have CloudWatch" with "developers can actually debug their own service." Raw CloudWatch Logs access, without structure, means a developer during an incident is grep-ing through a shared log group trying to find their service's output among everyone else's. A real observability layer means logs, metrics, and traces are automatically tagged and filtered by service and team ownership the moment they're emitted, through consistent log group naming conventions enforced by the provisioning layer itself, plus structured logging standards (JSON logs with a consistent service and team field) enforced at the application template level.

CloudWatch Container Insights gives you a reasonable baseline for EKS or ECS workload-level metrics without extra instrumentation, but at scale most platform teams end up centralizing into an aggregation layer, whether that's CloudWatch Logs Insights queries pre-built into dashboards per service, or shipping to a third-party observability platform via a Fluent Bit or OpenTelemetry Collector sidecar. Whichever you choose, the platform investment is in making the default experience for a new service "you already have dashboards and structured logs" rather than "here's how to set up your own observability if you want it."

Rendering diagram…

Enjoying this so far?

Join thousands of engineers receiving practical engineering, Cloud, Distributed Systems, Infrastructure and AI engineering insights.

No spam. Unsubscribe anytime.

Tradeoffs & Decision Framework

Build order, provisioning first or deployment first: Deployment tooling is more visible and easier to demo to leadership, which is why teams build it first. But provisioning is the actual bottleneck for anything net-new, and an excellent deployment pipeline sitting on top of a manual provisioning process just moves the two-week wait somewhere less visible. If you can only build one well in your first quarter, build provisioning, deployment friction is annoying, provisioning friction blocks entire projects from starting.

Centralized platform team vs embedded platform engineers: A fully centralized platform team gives you consistency and a single point of ownership, but risks becoming a bottleneck and a ticket queue if headcount doesn't scale with the number of teams it serves. Embedding platform engineers within product teams scales better for velocity but risks fragmenting standards: each embedded engineer solving the same problem slightly differently. Most orgs that get this right run a small centralized core (owning the shared modules, the golden paths, the guardrails) with product teams contributing back to the shared platform rather than forking it.

How much of the platform to buy vs build: Managed options (AWS Proton for environment templating, Backstage-as-a-service offerings, or full PaaS layers), reduce build time significantly but constrain you to their opinions about what a platform should look like. For orgs with genuinely unusual compliance or architecture requirements, that constraint becomes a real cost. For most teams under 40-50 engineers, buying the parts that are genuinely commodity (CI/CD orchestration, secrets rotation tooling) and building the parts that encode your specific org standards (the Terraform modules, the golden paths) is the more defensible allocation of platform engineering time.

Lessons From the Field

  1. After inheriting a platform initiative that had built an excellent deployment UI but no provisioning automation, we found the platform team was still fielding 15-20 manual infrastructure tickets a week: the flashy part of the platform was masking the actual bottleneck.

  2. In a multi-account migration for a healthcare client, a poorly scoped Service Control Policy applied at the wrong OU level broke Lambda invocations across four unrelated teams simultaneously, test every SCP against a sandbox OU before touching anything with real workloads on it.

  3. I've seen platform teams default every secret to Parameter Store to save cost, then spend a painful quarter retrofitting Secrets Manager rotation onto database credentials after a compliance audit flagged the lack of automatic rotation, decide on rotation needs per-secret, on day one.

  4. The most successful platform rollout I've been part of started with a single Terraform module for "a stateless service with a database" and expanded from actual demand, not a roadmap built in a vacuum, build the second module because a team asked for it, not because you assumed they would.

  5. Every time I've seen a centralized platform team become a genuine bottleneck, the root cause was the same: headcount stayed flat while the number of teams depending on the platform tripled, and nobody revisited the operating model until the ticket queue made it undeniable.

Final Thoughts

The internal developer platform trend is going to keep maturing past the current wave of "just buy Backstage and call it done" thinking, because the platforms that actually work are the ones built around a specific org's provisioning, deployment, configuration, and observability needs: not a generic template. The teams that get this right treat the platform itself as a product with real users and real feedback loops, not a side project the infrastructure team does between tickets.

This is the kind of platform architecture we help teams design and build at PulseSoft, if you're trying to figure out what your internal developer platform should actually consist of before you start building it, let's talk.

Key Takeaways

  • An internal developer platform is a specific set of systems (provisioning, deployment, configuration/secrets, observability, and often cost governance), not a single tool or a vague cultural goal.
  • Build the provisioning layer before the deployment layer if you can only prioritize one first, provisioning friction blocks entire projects from starting, while deployment friction just slows existing ones down.
  • Opinionated, versioned Terraform modules that encode org standards (encryption, tagging, IAM boundaries) are what makes provisioning genuinely self-service, not raw Terraform handed to developers.
  • Service Control Policies are deny-by-default and never grant permissions, test any new SCP against a sandboxed OU before applying it broadly, since a bad SCP can silently break unrelated accounts.
  • Decide between Parameter Store and Secrets Manager per-secret based on rotation needs, not platform-wide by default, retrofitting rotation onto existing credentials later is a much larger project than deciding correctly up front.
  • A real observability layer means logs and metrics are automatically scoped to service and team ownership by default, not just aggregated into a shared CloudWatch account developers have to filter manually.
  • Centralized platform teams need headcount that scales with the number of teams they serve, or they become the exact bottleneck the platform was built to eliminate.

Get insights like this in your inbox

Join thousands of engineers receiving practical engineering, Cloud, Distributed Systems, Infrastructure and AI engineering insights.

No spam. Unsubscribe anytime.

← Back to Blog