<!-- Canonical: https://lucioduran.com/es/blog/platform-engineering-idp-backstage-port
Language: es
Content-SHA256: a9b01653ecff0fdd0862e4256c0007ca37876e27f26cfef240db171c5b13424d
-->

DevOps

# Platform Engineering: Construyendo IDPs con Backstage y Port

2025-12-279 min lectura

Lucio Durán
Engineering Manager & AI Solutions Architect

También disponible en: [English](<https://lucioduran.com/blog/platform-engineering-idp-backstage-port>)

## El Estado del Tooling Interno

El tooling interno en la mayoría de las empresas consiste en scripts de Bash a medio terminar y páginas de Confluence que nadie actualiza desde 2023. Los desarrolladores no usan el camino "oficial" porque lo diseñó alguien que se fue hace año y medio, y el canal de Slack de soporte tiene tres miembros, dos de los cuales son bots.

Platform engineering no es construir otra herramienta. Es construir un producto, uno donde los clientes se encuentran en la misma oficina (o la misma llamada de Zoom) que el equipo de plataforma.

## Backstage: El Framework Open-Source

Spotify liberó [Backstage](<https://backstage.io/>) en 2020 y se convirtió en el framework de facto para developer portals. Para ser claros: Backstage sin configuración adicional es un catálogo de servicios con una UI bien diseñada. El poder real proviene de los plugins, y ahí es donde reside la inversión de ingeniería.

### Levantando una Instancia de Backstage

La ceremonia del ` npx @backstage/create-app ` se puede omitir aquí, los docs lo cubren bien. Lo que no cubren bien es cómo estructurar una instancia para una organización real.

```yaml
# app-config.yaml - configuración production-grade
app:
 title: "Acme Developer Portal"
 baseUrl: https://portal.acme.internal

backend:
 baseUrl: https://portal.acme.internal
 database:
 client: pg
 connection:
 host: ${POSTGRES_HOST}
 port: ${POSTGRES_PORT}
 user: ${POSTGRES_USER}
 password: ${POSTGRES_PASSWORD}

catalog:
 rules:
 - allow: [Component, System, API, Resource, Location, Template, Domain, Group, User]
 providers:
 github:
 acmeOrg:
 organization: 'acme-corp'
 catalogPath: '/catalog-info.yaml'
 filters:
 repository: '.*'
 schedule:
 frequency: { minutes: 30 }
 timeout: { minutes: 3 }

 locations:
 - type: url
 target: https://github.com/acme-corp/backstage-catalog/blob/main/org.yaml
```

La pieza crítica es el discovery provider de GitHub. En lugar de registrar cada servicio manualmente, se permite que Backstage recorra la organización de GitHub y encuentre los ` catalog-info.yaml `. Es el único enfoque que escala más allá de veinte servicios.

### Un catalog-info.yaml de Producción

La mayoría de los ejemplos muestran un spec mínimo. A continuación se presenta un ejemplo real después de seis meses operando Backstage, cuando se vuelve claro qué metadata resulta relevante:

```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
 name: payment-service
 description: "Maneja procesamiento de pagos, reembolse es y facturación de suscripciones"
 annotations:
 github.com/project-slug: acme-corp/payment-service
 backstage.io/techdocs-ref: dir:.
 pagerduty.com/service-id: P1A2B3C
 sonarqube.org/project-key: acme_payment-service
 backstage.io/kubernetes-id: payment-service
 jenkins.io/job-full-name: acme/payment-service/main
 tags:
 - java
 - payments
 - pci
 links:
 - url: https://grafana.acme.internal/d/payment-svc
 title: Grafana Dashboard
 icon: dashboard
 - url: https://runbooks.acme.internal/payment-service
 title: Runbook
 icon: docs
spec:
 type: service
 lifecycle: production
 owner: team-payments
 system: billing-platform
 dependsOn:
 - resource:default/payments-db
 - component:default/notification-service
 providesApis:
 - payment-api
 consumesApis:
 - stripe-gateway-api
```

El bloque de ` annotations ` es donde pasa la magia. Cada annotation conecta Backstage con un sistema externo, PagerDuty para guardia, SonarQube para calidad de código, Kubernetes para estado en runtime. Sin esto, tu developer portal es una planilla de Excel glorificada.

## Port: La Alternativa Declarativa

[Port](<https://www.getport.io/>) toma un enfoque fundamentalmente distinto. En lugar de plugins y código, se define el modelo de datos como blueprints, esencialmente schemas para las entidades de la developer platform.

```json
{
 "identifier": "microservice",
 "title": "Microservice",
 "icon": "Microservice",
 "schema": {
 "properties": {
 "language": {
 "type": "string",
 "enum": ["Go", "Java", "Python", "Node.js", "Rust"],
 "title": "Language"
 },
 "tier": {
 "type": "string",
 "enum": ["critical", "standard", "experimental"],
 "title": "Service Tier",
 "description": "Determina SLO targets y requerimientos de guardia"
 },
 "repository_url": {
 "type": "string",
 "format": "url",
 "title": "Repository"
 },
 "dora_lead_time_hours": {
 "type": "number",
 "title": "Lead Time (horas)"
 },
 "resource_cost_monthly": {
 "type": "number",
 "title": "Costo Mensual ($)"
 }
 },
 "required": ["language", "tier", "repository_url"]
 },
 "relations": {
 "team": {
 "target": "team",
 "required": true,
 "title": "Equipo Owner"
 },
 "environment": {
 "target": "environment",
 "many": true,
 "title": "Ambientes Deployados"
 }
 }
}
```

Una fortaleza genuina de Port es que permite modelar cosas que en Backstage son medio incómodas. Atribución de costos, estado de compliance, métricas DORA, se vuelven propiedades de primera clase en vez de annotations que teners que parsear.

### Self-Service Actions en Port

Acá es donde Port brilla. Puede crear acciones de autoservicio que disparan cambios reales de infraestructura:

```json
{
 "identifier": "provision_database",
 "title": "Provisionar Base de Datos PostgreSQL",
 "trigger": "CREATE",
 "invocationMethod": {
 "type": "GITHUB",
 "org": "acme-corp",
 "repo": "platform-actions",
 "workflow": "provision-db.yml"
 },
 "userInputs": {
 "properties": {
 "db_name": {
 "type": "string",
 "pattern": "^[a-z][a-z0-9_]{2,30}$",
 "title": "Nombre de la Base"
 },
 "instance_size": {
 "type": "string",
 "enum": ["small", "medium", "large"],
 "default": "small",
 "title": "Tamaño de Instancia"
 },
 "enable_read_replica": {
 "type": "boolean",
 "default": false,
 "title": "Habilitar Read Replica"
 }
 },
 "required": ["db_name", "instance_size"]
 }
}
```

El developer hace click en un botón, llena un formulario, y un workflow de GitHub Actions provisiona una instancia real de RDS con Terraform. Sin tickets, sin esperar al equipo de infra, sin DMs de Slack a las 4 de la tarde un viernes.

## Golden Paths: El Corazón del Platform Engineering

Un golden path es un camino pre-pavimentado y con opiniones para hacer algo. No es el único camino, es el recomendado. La distinción importa.

Acá va un Software Template de Backstage que implementa un golden path para crear un nuevo microservicio:

```yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
 name: go-microservice
 title: Go Microservice (Golden Path)
 description: "Servicio Go production-ready con CI/CD, observabilidad y manifests de Kubernetes"
 tags:
 - go
 - recommended
 - golden-path
spec:
 owner: team-platform
 type: service
 parameters:
 - title: Detalles del Servicio
 required:
 - name
 - owner
 - tier
 properties:
 name:
 title: Nombre del Servicio
 type: string
 pattern: '^[a-z][a-z0-9-]{2,30}$'
 ui:help: 'Minúsculas, solo guiones. Esto se convierte en el nombre del repo, namespace de k8s y entrada DNS.'
 owner:
 title: Equipo Owner
 type: string
 ui:field: OwnerPicker
 ui:options:
 catalogFilter:
 kind: Group
 tier:
 title: Tier del Servicio
 type: string
 enum: ['critical', 'standard', 'experimental']
 enumNames: ['Crítico (99.9% SLO)', 'Estándar (99.5% SLO)', 'Experimental (best-effort)']
 steps:
 - id: fetch
 name: Fetch Template
 action: fetch:template
 input:
 url: ./skeleton
 values:
 name: ${{ parameters.name }}
 owner: ${{ parameters.owner }}
 tier: ${{ parameters.tier }}
 - id: publish
 name: Crear Repositorio
 action: publish:github
 input:
 allowedHosts: ['github.com']
 repoUrl: 'github.com?owner=acme-corp&repo=${{ parameters.name }}'
 defaultBranch: main
 protectDefaultBranch: true
 requireCodeOwnerReviews: true
 - id: register
 name: Registrar en Catálogo
 action: catalog:register
 input:
 repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
 catalogInfoPath: '/catalog-info.yaml'
```

En menos de dos minutos, un developer pasa de "necesito un servicio nuevo" a tener un repositorio con pipelines de CI/CD, manifests de Kubernetes, un Dockerfile, logging estructurado, health checks, métricas de Prometheus y un dashboard de Grafana, todo pre-configurado y siguiendo los estándares de la organización.

## Métricas de Developer Experience Relevantes

No se puede mejorar lo que no se mide, y la mayoría de los platform teams miden las cosas equivocadas. "Cantidad de page views en Backstage" no ofrece información accionable. Las siguientes métricas son las que vale la pena monitorear:

```python
# developer_experience_metrics.py
from dataclasses import dataclass
from datetime import timedelta

@dataclass
class PlatformMetrics:
 # Tiempo desde "necesito X" hasta "X está provisionado y usable"
 time_to_first_request: timedelta

 # Porcentaje de servicios nuevos usando golden path templates
 golden_path_adoption_rate: float

 # Cuántas veces los desarrolladores tuvieron que pedir ayuda en Slack
 # vs se auto-sirvieron por el portal
 self_service_ratio: float

 # DORA: tiempo desde commit hasta producción
 lead_time_for_changes: timedelta

 # Cuántas veces un dev abandona un flujo de self-service a mitad
 action_abandonment_rate: float

 # Net Promoter Score de encuestas trimestrales a developers
 developer_nps: int

# Números reales de un engagement con cliente (anonimizado)
before_idp = PlatformMetrics(
 time_to_first_request=timedelta(days=5),
 golden_path_adoption_rate=0.0,
 self_service_ratio=0.12,
 lead_time_for_changes=timedelta(days=14),
 action_abandonment_rate=0.0, # no existía self-service
 developer_nps=-23
)

after_idp_6_months = PlatformMetrics(
 time_to_first_request=timedelta(minutes=8),
 golden_path_adoption_rate=0.73,
 self_service_ratio=0.81,
 lead_time_for_changes=timedelta(hours=4),
 action_abandonment_rate=0.15,
 developer_nps=41
)
```

Los números del antes/después de ese engagement son llamativos. Cinco días a ocho minutos para provisionar. Pero el que convenció a la gerencia fue el swing del NPS, de -23 a +41. Los developers pasaron de estar activamente hostiles con las herramientas internas a recomendar la plataforma a los nuevos.

## Dónde Se Rompe Todo

No todo es color de rosa. Estos son los problemas comunes:

**La calidad de los plugins de Backstage es salvajemente inconsistente.** Algunos plugins de la comunidad son excelentes. Otros no se actualizan hace un año y se rompen con cada upgrade de Backstage. Presupuestar tiempo para mantenimiento de plugins o armar los tuyos propios.

**El pricing de Port escala con entidades.** Cuando se empieza a modelar cada pod de Kubernetes, cada Lambda function, cada bucket de S3 como entidad, la factura crece rápido. Ser deliberado con lo que se modela.

**Lo más difícil no es técnico.** Es convencer a los equipos de registrar sus servicios en el catálogo y mantener la metadata actualizada. Una solución probada es un GitHub Action que falla el CI si falta el ` catalog-info.yaml ` o tiene info de ownership vieja:

```yaml
# .github/workflows/catalog-lint.yml
name: Catalog Info Lint
on: [pull_request]
jobs:
 lint:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v4
 - name: Validar catalog-info.yaml
 run: |
 if [ ! -f catalog-info.yaml ]; then
 echo "::error::Falta catalog-info.yaml. Ver https://portal.acme.internal/docs/catalog-requirements"
 exit 1
 fi
 OWNER=$(yq '.spec.owner' catalog-info.yaml)
 if ! curl -sf "https://portal.acme.internal/api/catalog/entities/by-name/group/default/${OWNER}" > /dev/null; then
 echo "::error::Owner '${OWNER}' no encontrado en el catálogo de Backstage. Actualizá spec.owner."
 exit 1
 fi
```

## Recomendación

Para equipos de menos de 100 developers que quieren resultados en semanas, Port es el punto de partida más fuerte. El modelo de blueprints te da un IDP funcional rápido, y las acciones de self-service están production-ready de fábrica.

Para organizaciones de más de 100 developers, o que necesitan customización profunda u opiniones fuertes sobre ownership de datos, Backstage es la mejor opción. Pero el compromiso es esencial, una instancia de Backstage a medio construir es peor que no tener developer portal.

En cualquier caso, iniciar por el catálogo de servicios. Que el ownership esté bien. Después agregar golden paths. Después self-service actions. Tratar de hacer las tres cosas al mismo tiempo es cómo los platform teams se queman y los desarman.

Este patrón se repite en las organizaciones. Los equipos que tienen éxito tratan a la plataforma como un producto con usuarios reales, ciclos de feedback reales y roadmaps reales. Los que fallan la tratan como un proyecto con fecha límite y entrega. No hay entrega final. La plataforma nunca se termina.

## Preguntas frecuentes

### ¿Cuál es la diferencia entre Backstage y Port?

Backstage es un framework open-source que se aloja y extiende con plugins. Port es SaaS con un modelo declarativo de blueprints. Backstage ofrece control total pero requiere inversión en ingeniería; Port permite iniciar rápidamente pero con menos profundidad de personalización.

### ¿Cuántos ingenieros necesito para mantener un IDP con Backstage?

De manera realista, planificar 1-2 platform engineers dedicados por cada 50-80 desarrolladores de aplicación. Con Port el ratio baja porque ellos manejan la infraestructura, pero igualmente se necesita alguien a cargo de blueprints e integraciones.

### ¿Los golden paths son obligatorios para todos los equipos?

No. Los golden paths tienen que ser el camino de menor resistencia, no un mandato. Los equipos que necesiten desviarse deberían poder, pero la fricción tiene que ser intencional y documentada.

## Tips

1. **Tip:** Antes de construir nada, medir las ` DORA metrics `. Sin una baseline se está trabajando a ciegas.
2. **Tip:** Utilizar el ` catalog-info.yaml ` de Backstage como fuente de verdad para la propiedad de servicios. Si no está en el catálogo, no existe.
3. **Tip:** Los golden paths necesitan escape hatches. Si un desarrollador *necesita* desviarse, debe poder hacerlo de forma visible, ` softGate: true ` en el blueprint de Port.

- platform-engineering
- backstage
- port
- idp
- developer-experience
- golden-paths
- self-service

[Volver al Blog](<https://lucioduran.com/es/blog>)
