Contract-First Design Skill
When to Use This Skill
Use this skill when:
-
Contract First Design tasks - Working on design and manage api contracts before implementation using openapi and asyncapi specifications for contract-first development
-
Planning or design - Need guidance on Contract First Design approaches
-
Best practices - Want to follow established patterns and standards
Overview
Apply contract-first development methodology for APIs, ensuring specifications drive implementation.
Contract-First Methodology
Core Principles
contract_first_principles: design_before_code: description: "API specification comes before implementation" benefits: - "Early feedback from consumers" - "Parallel development enabled" - "Clear contract for testing" - "Documentation from day one"
specification_as_source_of_truth: description: "Spec is authoritative, code conforms to it" enforcement: - "Generate code from spec" - "Validate implementation against spec" - "CI/CD gates on spec compliance"
consumer_centric: description: "Design for consumer needs, not provider convenience" practices: - "Involve consumers in design reviews" - "Consumer-driven contract testing" - "Gather real-world usage patterns"
evolution_over_revolution: description: "Evolve contracts without breaking consumers" practices: - "Semantic versioning" - "Backward compatibility by default" - "Deprecation before removal"
Development Workflow
contract_first_workflow: phases: 1_design: activities: - "Identify API consumers and use cases" - "Define resources and operations" - "Draft specification (OpenAPI/AsyncAPI)" - "Review with stakeholders" artifacts: - "Draft API specification" - "Use case documentation" gate: "Specification approved by consumers"
2_validate:
activities:
- "Lint specification for style/standards"
- "Check backward compatibility"
- "Generate mock server"
- "Consumer acceptance testing with mocks"
artifacts:
- "Lint report"
- "Compatibility report"
- "Mock server configuration"
gate: "Consumers validated against mocks"
3_implement:
activities:
- "Generate server stubs"
- "Implement business logic"
- "Contract testing against spec"
- "Integration testing"
artifacts:
- "Generated code"
- "Contract test results"
gate: "Implementation passes contract tests"
4_publish:
activities:
- "Publish specification to catalog"
- "Generate documentation"
- "Update changelog"
- "Notify consumers"
artifacts:
- "Published specification"
- "API documentation portal"
- "Changelog entry"
gate: "Documentation live, consumers notified"
5_operate:
activities:
- "Monitor API usage"
- "Collect consumer feedback"
- "Track breaking change requests"
- "Plan next version"
artifacts:
- "Usage metrics"
- "Feedback log"
- "Deprecation schedule"
Contract Management
Specification Organization
specification_organization: directory_structure: recommended: specs/ openapi/ order-service.yaml customer-service.yaml inventory-service.yaml asyncapi/ order-events.yaml inventory-events.yaml shared/ schemas/ common-types.yaml error-responses.yaml parameters/ pagination.yaml security/ auth-schemes.yaml
file_naming: pattern: "{service-name}.yaml" versioned: "{service-name}-v{major}.yaml"
modular_specs: description: "Split large specs into components" approach: main_file: "Defines paths, references components" components_dir: "Reusable schemas, parameters, responses" shared_dir: "Cross-API shared definitions"
example_main:
openapi: "3.1.0"
info:
title: "Order Service API"
version: "1.0.0"
paths:
$ref: "./paths/orders.yaml"
components:
schemas:
$ref: "./schemas/_index.yaml"
Version Management
version_management: semantic_versioning: major: "Breaking changes" minor: "Backward-compatible additions" patch: "Backward-compatible fixes"
version_in_spec: location: "info.version" format: "MAJOR.MINOR.PATCH"
api_versioning_strategies: url_path: spec_example: servers: - url: "https://api.example.com/v1" change_approach: "New spec file for major versions"
header:
spec_example:
parameters:
API-Version:
in: header
required: false
schema:
type: string
default: "2025-01-01"
changelog_requirements: location: "CHANGELOG.md alongside spec" format: "Keep a Changelog" content: - "Version number and date" - "Added: new endpoints/fields" - "Changed: modified behavior" - "Deprecated: marked for removal" - "Removed: breaking deletions" - "Fixed: bug fixes" - "Security: vulnerability patches"
Breaking Change Detection
breaking_changes: definition: "Changes that can break existing consumers"
openapi_breaking: removals: - "Remove endpoint" - "Remove required response field" - "Remove enum value" - "Remove supported content type"
modifications:
- "Change field type"
- "Add required request field"
- "Narrow validation (smaller max, larger min)"
- "Change authentication requirements"
renames:
- "Rename field (equivalent to remove + add)"
- "Change endpoint path"
asyncapi_breaking: removals: - "Remove channel" - "Remove message type" - "Remove required payload field"
modifications:
- "Change payload schema incompatibly"
- "Change channel address format"
- "Modify required headers"
detection_tools: openapi: - "openapi-diff" - "oasdiff" - "speccy" asyncapi: - "asyncapi/diff"
ci_integration: script: | # Compare current spec against main branch oasdiff breaking main.yaml current.yaml if [ $? -ne 0 ]; then echo "Breaking changes detected!" exit 1 fi
C# Models for Contract Management
namespace SpecDrivenDevelopment.ContractFirst;
/// <summary> /// Represents an API contract lifecycle state /// </summary> public enum ContractStatus { Draft, InReview, Approved, Implementing, Published, Deprecated, Retired }
/// <summary> /// API contract metadata /// </summary> public record ApiContract { public required string Id { get; init; } public required string Name { get; init; } public required string Version { get; init; } public required ContractType Type { get; init; } public required ContractStatus Status { get; init; } public required string SpecificationPath { get; init; } public string? Description { get; init; } public List<string> Owners { get; init; } = []; public List<string> Consumers { get; init; } = []; public DateTimeOffset CreatedAt { get; init; } public DateTimeOffset? PublishedAt { get; init; } public DateTimeOffset? DeprecatedAt { get; init; } public DateTimeOffset? SunsetAt { get; init; } }
public enum ContractType { OpenApi, AsyncApi, GraphQL, gRPC }
/// <summary> /// Tracks changes between contract versions /// </summary> public record ContractChange { public required string ContractId { get; init; } public required string FromVersion { get; init; } public required string ToVersion { get; init; } public required ChangeType Type { get; init; } public required BreakingLevel Breaking { get; init; } public required string Path { get; init; } public required string Description { get; init; } public string? MigrationGuide { get; init; } }
public enum ChangeType { Added, Modified, Deprecated, Removed }
public enum BreakingLevel { None, Minor, // Backward compatible Major // Breaking change }
/// <summary> /// Contract validation result /// </summary> public record ContractValidationResult { public required bool IsValid { get; init; } public List<ValidationIssue> Issues { get; init; } = []; public List<ContractChange> Changes { get; init; } = []; public bool HasBreakingChanges => Changes.Any(c => c.Breaking == BreakingLevel.Major); }
public record ValidationIssue { public required ValidationSeverity Severity { get; init; } public required string Code { get; init; } public required string Message { get; init; } public required string Path { get; init; } public string? Suggestion { get; init; } }
public enum ValidationSeverity { Error, Warning, Info }
/// <summary> /// Consumer contract registration /// </summary> public record ConsumerContract { public required string ConsumerId { get; init; } public required string ConsumerName { get; init; } public required string ProviderId { get; init; } public required string ProviderVersion { get; init; } public List<string> UsedEndpoints { get; init; } = []; public List<string> UsedSchemas { get; init; } = []; public DateTimeOffset RegisteredAt { get; init; } public DateTimeOffset? LastVerifiedAt { get; init; } }
/// <summary> /// Service for contract management /// </summary> public interface IContractService { Task<ApiContract> CreateContractAsync(CreateContractRequest request); Task<ApiContract> GetContractAsync(string id); Task<IReadOnlyList<ApiContract>> ListContractsAsync(ContractFilter? filter = null); Task<ContractValidationResult> ValidateContractAsync(string id); Task<ContractValidationResult> CompareVersionsAsync(string id, string fromVersion, string toVersion); Task PublishContractAsync(string id); Task DeprecateContractAsync(string id, DateTimeOffset sunsetDate); Task RegisterConsumerAsync(string contractId, ConsumerContract consumer); Task<IReadOnlyList<ConsumerContract>> GetConsumersAsync(string contractId); }
public record CreateContractRequest { public required string Name { get; init; } public required ContractType Type { get; init; } public required string SpecificationContent { get; init; } public string? Description { get; init; } public List<string> Owners { get; init; } = []; }
public record ContractFilter { public ContractType? Type { get; init; } public ContractStatus? Status { get; init; } public string? Owner { get; init; } public string? Consumer { get; init; } }
Contract Testing
Provider Verification
provider_verification: description: "Verify implementation matches specification"
approaches: schema_validation: description: "Validate request/response against spec" tools: - "express-openapi-validator" - "NSwag middleware" - "Spectral"
contract_tests:
description: "Test endpoints match spec exactly"
tools:
- "Dredd"
- "Schemathesis"
- "Prism"
dotnet_example: integration_test: | [Fact] public async Task GetOrder_ReturnsValidResponse() { // Arrange var spec = await OpenApiDocument.FromFileAsync("specs/order-service.yaml"); var validator = new OpenApiValidator(spec);
// Act
var response = await _client.GetAsync("/orders/123");
var body = await response.Content.ReadAsStringAsync();
// Assert
var result = validator.ValidateResponse(
"/orders/{orderId}",
"get",
(int)response.StatusCode,
body);
Assert.True(result.IsValid, result.ErrorMessage);
}
ci_pipeline: steps: - "Load OpenAPI/AsyncAPI spec" - "Start application under test" - "Run contract tests against all endpoints" - "Fail build if any contract violation"
Consumer-Driven Contracts
consumer_driven_contracts: description: "Consumers define expected provider behavior"
workflow: 1_consumer_defines: action: "Consumer creates contract with expected interactions" artifact: "Pact file or similar contract"
2_provider_verifies:
action: "Provider runs consumer contracts"
validation: "Provider can satisfy all consumer expectations"
3_publish:
action: "Publish verified contracts to broker"
enables: "Can-I-Deploy checks"
pact_example: consumer_test: | [Fact] public async Task GetOrder_ExpectedBehavior() { var pact = Pact.V3("OrderConsumer", "OrderProvider");
pact.Given("Order 123 exists")
.UponReceiving("A request for order 123")
.WithRequest(HttpMethod.Get, "/orders/123")
.WillRespond()
.WithStatus(200)
.WithJsonBody(new
{
id = "123",
status = Match.Type("pending"),
totalAmount = Match.Decimal(99.99m)
});
await pact.VerifyAsync(async ctx =>
{
var client = new OrderClient(ctx.MockServerUri);
var order = await client.GetOrderAsync("123");
Assert.NotNull(order);
});
}
asyncapi_contracts: approach: "Message schema contracts" verification: - "Producer publishes valid messages" - "Consumer can deserialize all message versions" - "Schema registry enforces compatibility"
API Governance
Style Guidelines
api_style_guidelines: naming: resources: - "Use plural nouns (users, orders, products)" - "Use kebab-case for multi-word (order-items)" - "Avoid verbs in resource names"
operations:
- "operationId: camelCase (getUser, createOrder)"
- "Consistent verb usage across APIs"
fields:
- "camelCase for JSON properties"
- "Consistent date format (ISO 8601)"
- "Use standard field names (id, createdAt, updatedAt)"
structure: versioning: "URL path versioning (/v1/)" pagination: "Cursor-based or offset, consistent approach" errors: "RFC 7807 Problem Details" filtering: "Query parameters with consistent naming"
security: authentication: "OAuth 2.0 / API Key as standard" authorization: "Scopes documented in spec" sensitive_data: "Never in URL parameters"
documentation: required: - "Summary for every operation" - "Description for complex operations" - "Examples for request/response bodies" - "Error response documentation"
Automated Enforcement
automated_enforcement: linting: tools: - "Spectral (OpenAPI)" - "AsyncAPI Studio" - "Redocly CLI"
spectral_rules: |
extends: ["spectral:oas", "spectral:asyncapi"]
rules:
operation-operationId:
severity: error
operation-summary:
severity: error
operation-tags:
severity: warn
info-contact:
severity: error
response-error-format:
description: "Errors must use RFC 7807"
severity: error
given: "$.paths.*.*.responses[?(@property >= '400')]"
then:
field: content.application/problem+json
function: truthy
breaking_change_detection:
ci_step: |
- name: Check Breaking Changes
run: |
oasdiff breaking
--base main:specs/api.yaml
--revision HEAD:specs/api.yaml
--fail-on ERR
pre_commit_hooks: config: | repos: - repo: local hooks: - id: lint-openapi name: Lint OpenAPI entry: spectral lint specs/**/*.yaml language: node types: [yaml]
API Catalog
api_catalog: purpose: "Central registry of all API contracts"
features: discovery: "Find APIs by name, domain, capability" documentation: "Auto-generated from specs" versioning: "Track all versions and changes" dependencies: "Consumer/provider relationships" metrics: "Usage, errors, latency"
metadata_per_api: identity: - "Name and description" - "Owner team" - "Domain/capability" lifecycle: - "Current version" - "Status (draft/published/deprecated)" - "Sunset date if deprecated" consumers: - "Registered consumers" - "Usage statistics" quality: - "Documentation score" - "Test coverage" - "Breaking change history"
integration: specification_source: "Git repository" ci_cd: "Publish on merge to main" portal: "Developer portal generation"
Validation Checklist
contract_first_checklist: pre_design: - "Consumer use cases documented" - "Resource model defined" - "Authentication/authorization strategy chosen" - "Versioning strategy agreed"
specification: - "Valid OpenAPI/AsyncAPI syntax" - "Passes linting rules" - "All operations have operationId" - "Examples provided for complex types" - "Error responses documented" - "Security schemes defined"
review: - "Consumer representatives reviewed" - "No unnecessary breaking changes" - "Backward compatibility verified" - "Documentation complete"
implementation: - "Generated stubs used" - "Contract tests passing" - "Response validation enabled" - "Schema changes go through spec first"
publication: - "Changelog updated" - "Catalog entry created" - "Consumers notified" - "Deprecation warnings if applicable"
References
-
Related skill: openapi-authoring
-
OpenAPI specification authoring
-
Related skill: asyncapi-authoring
-
AsyncAPI specification authoring
-
Related skill: gherkin-authoring
-
Acceptance criteria in Gherkin format
Last Updated: 2025-12-26