Pacto for Developers
As a developer, you own the service contract. Pacto gives you a structured way to declare your service’s operational interface alongside your code — ensuring that platform engineers, CI systems, and other teams have an accurate, machine-readable description of what your service needs to run.
Table of contents
Your workflow
flowchart LR
A[Write code] --> B[Define pacto.yaml]
B --> C[pacto validate]
C --> D[pacto pack]
D --> E[pacto push]
E --> F[CI / Platform picks it up]
1. Initialize your contract
pacto init my-service
This scaffolds a contract with sensible defaults. Edit pacto.yaml to match your service.
2. Declare your interfaces
List every boundary your service exposes:
interfaces:
- name: api
type: http
port: 8080
visibility: public
contract: interfaces/openapi.yaml
- name: events
type: event
visibility: internal
contract: interfaces/events.yaml
Include the actual interface files (OpenAPI specs, protobuf definitions, event schemas) in the bundle.
3. Define your runtime semantics
This is where you tell the platform how your service behaves:
runtime:
workload: service
state:
type: stateless
persistence:
scope: local
durability: ephemeral
dataCriticality: low
health:
interface: api
path: /health
Ask yourself:
- Is my service long-running (
service) or does it run to completion (job)? - Does it hold local state that survives restarts (
stateful) or not (stateless)? - How critical is the data it handles?
4. Declare dependencies
If your service depends on other Pacto-enabled services:
dependencies:
- ref: ghcr.io/acme/auth-pacto@sha256:abc123
required: true
compatibility: "^2.0.0"
- ref: ghcr.io/acme/cache-pacto:1.0.0
required: false
compatibility: "~1.0.0"
Use pacto graph to visualize your dependency tree.
5. Validate before pushing
pacto validate my-service/pacto.yaml
Validation catches errors in three layers:
- Structural — missing fields, wrong types, invalid enum values
- Cross-field — interface references match, state invariants hold, files exist
- Semantic — strategy consistency warnings
6. Pack and push
pacto pack my-service/pacto.yaml
pacto push ghcr.io/your-org/my-service-pacto:1.0.0 -p my-service/pacto.yaml
Common patterns
Stateless HTTP API
runtime:
workload: service
state:
type: stateless
persistence:
scope: local
durability: ephemeral
dataCriticality: low
health:
interface: api
path: /health
scaling:
min: 2
max: 10
Stateful service (database proxy, cache)
runtime:
workload: service
state:
type: stateful
persistence:
scope: local
durability: persistent
dataCriticality: high
lifecycle:
upgradeStrategy: ordered
gracefulShutdownSeconds: 60
health:
interface: api
path: /health
scaling:
min: 3
max: 5
Scheduled job
runtime:
workload: scheduled
state:
type: stateless
persistence:
scope: local
durability: ephemeral
dataCriticality: low
health:
interface: api
path: /health
# No scaling — jobs don't scale horizontally
Detecting breaking changes
Before releasing a new version, diff against the previous one:
pacto diff ghcr.io/acme/my-service-pacto:1.0.0 my-service/pacto.yaml
Classification: BREAKING
Changes (2):
[BREAKING] interfaces (removed): metrics
[NON_BREAKING] service.version (modified): service.version modified
Integrate pacto diff into your CI pipeline to block merges that introduce breaking changes.
Tips
- Version your contract alongside your code. The
pacto.yamllives in your repository. - Pin dependency digests in production. Tags are mutable; digests are not.
- Keep interface contracts up to date. OpenAPI specs and protobuf definitions in the bundle should match what your service actually serves.
- Use
pacto explainto review. It produces a human-readable summary of your contract. - Use metadata for organizational context. Team ownership, on-call channels, and service tiers go in
metadata.