skip to content

Tracing, Contracts & Functions

The cross-cutting Spring Cloud pieces: distributed tracing, consumer-driven contracts, and transport-agnostic functions. Interviewers reach here when the conversation turns to debugging and testing across service boundaries.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

15

What problem does Spring Cloud Contract solve, and what is a consumer-driven contract?

level: juniorimportance: must knowfreq 55%

answer

  1. producer verified, consumer stubbed
  2. one contract → tests + WireMock stubs
  3. Groovy/YAML/Kotlin DSL
  4. stubs JAR classifier=stubs
  5. shared source of truth vs drifting mocks

basics

~20 s

Spring Cloud Contract keeps a service (producer) and its callers (consumers) in sync. A contract is a shared file describing a request and the expected response. It generates producer tests and consumer stubs from that one file.

solid answer

~40 s

Spring Cloud Contract does contract testing between an API provider (producer) and its callers (consumers) without a slow end-to-end setup. A 'contract' is a single file (Groovy DSL, YAML, or Kotlin DSL) describing an HTTP request and the expected response. From it the framework does two things: on the producer it generates tests that fail if the real controller stops honoring the contract, and it packages WireMock stubs that consumers run their tests against. 'Consumer-driven' means the contract expresses what consumers actually need, so the producer can't silently break them. It gives you the fast feedback of unit tests with confidence closer to integration tests, and both sides test against the same source of truth instead of hand-written mocks that drift.

code

groovy · 18 lines
groovy
// src/test/resources/contracts/shouldReturnUser.groovy
import org.springframework.cloud.contract.spec.Contract

Contract.make {
    description("should return a user by id")
    request {
        method GET()
        url '/users/42'
    }
    response {
        status OK()  // 200
        headers { contentType(applicationJson()) }
        body([
            id  : 42,
            name: 'Ada'
        ])
    }
}

go deeper

for a junior

Know the one-liner: one contract file produces producer tests + consumer stubs, keeping both sides in sync.

for a middle

Be able to name the DSL formats and describe the two generated artifacts and where they run.

for a senior

Explain 'consumer-driven', the shared-source-of-truth benefit over hand-written mocks, and when contract testing beats E2E.

for a principal

Position SCC vs OpenAPI/Pact, discuss contract ownership/governance, and where it fits in a broader test pyramid strategy.

## The problem In a microservice system, a **producer** exposes an HTTP (or messaging) API and one or more **consumers** call it. Two failure modes are common: (1) the consumer's hand-written mock of the producer drifts from reality, so consumer tests pass but production breaks; (2) the producer changes its API and unknowingly breaks a consumer. Full end-to-end tests catch this but are slow, flaky, and require every service running together. ## Contract testing Spring Cloud Contract (SCC) is Spring's implementation of **Consumer-Driven Contracts (CDC)**. A **contract** is a declarative file that pins down: an incoming **request** (method, URL, headers, body) and the **response** the producer must return (status, headers, body). Contracts are written in a **Groovy DSL**, **YAML**, or a **Kotlin DSL**, and live in the producer repository (typically `src/test/resources/contracts/...`). From that one file, SCC generates two artifacts: 1. **Producer-side generated tests.** The **Spring Cloud Contract Verifier** build plugin (Maven `spring-cloud-contract-maven-plugin` or the Gradle plugin) reads each contract at build time and code-generates a JUnit test. That test fires the contract's request at your running controller (via MockMvc/WebTestClient/RestAssured) and asserts the response matches. If your real implementation diverges from the contract, the build fails. This proves the producer actually satisfies the contract. 2. **Consumer-side stubs.** The same contract is compiled into **WireMock** stub mappings (JSON) and packaged into a **stubs JAR** (Maven classifier `stubs`). Consumers pull that JAR and run their tests against a local WireMock server serving exactly those responses — via **Stub Runner** and `@AutoConfigureStubRunner`. Because the stub is generated from the same contract the producer is tested against, consumer and producer share one source of truth. ## 'Consumer-driven' The contract ideally expresses what consumers need. In practice contracts can live in the producer repo or be contributed by consumers. The key property: the contract is verified on the producer, so a producer change that violates it fails the producer's own build. ## When to use - You own a producer with multiple internal consumers and want to catch breaking changes at build time. - You want fast, isolated tests without spinning up the whole system. Not ideal for public third-party APIs you don't control, or where OpenAPI-based tooling already governs the contract. ## Gotchas - A contract is not a schema; it's a concrete example (one request → one response), optionally with matchers for dynamic parts. - The producer test needs a **base class** to set up the controller under test; forgetting it is the most common first-time failure. - Stubs are only as good as the contracts — an untested field on the producer is invisible to consumers.

  • How is a Spring Cloud Contract different from an OpenAPI/Swagger spec?
    OpenAPI describes the shape/schema of an API for documentation and client generation. A contract is an executable example pair (request→response) that is verified against the running producer and turned into runnable stubs, so it actively fails builds on divergence rather than just documenting.
  • Does the contract file live with the producer or the consumer?
    Most commonly in the producer repo under src/test/resources/contracts, so the producer's build both generates its verification tests and publishes the stub JAR. Alternatively a shared external contracts repo can host them; consumers can also contribute contracts via PR to the producer.

saying these in an interview costs you the question

  • Thinking the contract only documents the API and isn't actually executed/verified.
  • Believing SCC spins up both services together like an end-to-end test.
  • Confusing stubs (consumer side) with the generated verification tests (producer side).

context

open as a page

What is Spring Cloud Function, and which three core interfaces does it build on?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Spring Cloud Function lets you write business logic as plain beans of type Function, Supplier, or Consumer (from java.util.function). The framework then exposes them over HTTP, messaging, or serverless without you writing transport code.

open as a page

What is distributed tracing, and what are trace IDs and span IDs in a Spring Boot application using Micrometer Tracing (formerly Spring Cloud Sleuth)?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Distributed tracing follows one request as it hops between services. A trace ID is a shared id for the whole request; a span id marks one step (like one service call). Micrometer Tracing adds both to logs automatically.

open as a page

How does the producer-side test generation work, and what is the base class for?

level: middleimportance: must knowfreq 50%

basics

~20 s

The Verifier build plugin reads each contract and generates a JUnit test that sends the contract's request to your controller and checks the response. You provide a base class that sets up the controller (MockMvc/RestAssured) so the generated test can run.

open as a page

On the consumer side, how does Stub Runner let you test against the producer's stubs?

level: middleimportance: must knowfreq 52%

basics

~20 s

You annotate a consumer test with @AutoConfigureStubRunner, list the producer's stub JAR coordinates, and Stub Runner downloads the JAR and starts a WireMock server serving those stubs. Your consumer code calls it as if it were the real producer.

open as a page

How is trace context propagated between services, and what is the difference between W3C traceparent and B3 propagation formats in Micrometer Tracing?

level: seniorimportance: must knowfreq 60%

basics

~20 s

The trace ID, span ID, and sample flag travel in HTTP headers. The caller injects them, the callee extracts them. W3C uses one traceparent header; B3 (from Zipkin) uses multiple X-B3-* headers or a single b3 header. You pick the format via configuration.

open as a page

How does Spring Cloud Function select and route which function to run, and how do you compose functions?

level: middleimportance: should knowfreq 45%

basics

~20 s

The active function is chosen by the spring.cloud.function.definition property (the bean name). You compose functions with the pipe symbol, e.g. "uppercase|reverse". For dynamic dispatch, RoutingFunction picks a target per message using a header or expression.

open as a page

How does the Micrometer Observation API create spans, and how do you create a custom span in a Spring Boot 3 application?

level: middleimportance: should knowfreq 55%

basics

~20 s

You wrap code in an Observation using the ObservationRegistry. Each observation becomes a span (and a metric). Start it, run your code inside observe(), and Micrometer Tracing opens a span with the current trace context as parent.

open as a page

How do you write a contract with dynamic values, and why are body matchers important?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Use the DSL's dynamic helpers so the request is matched by a pattern (regex) while the stub returns a concrete example. On the response, a fixed value doubles as both the returned value and the assertion; matchers let you assert by pattern instead of exact value.

open as a page

How does the same function bean deploy to AWS Lambda and Azure Functions without changing the business logic?

level: seniorimportance: should knowfreq 40%

basics

~20 s

You add a FaaS adapter dependency and point the cloud runtime at a generic handler that delegates to your function. For AWS you configure the handler as Spring Cloud Function's FunctionInvoker; for Azure you use the Azure adapter with an @FunctionName entry that calls into the FunctionCatalog. Your Function bean is unchanged.

open as a page

How does Spring Cloud Function handle payload type conversion, message headers, and reactive vs imperative signatures?

level: seniorimportance: should knowfreq 30%

basics

~20 s

The FunctionCatalog wraps each function and applies message converters, so a JSON payload is turned into your declared POJO and the result serialized back. To read headers, declare the input as Message<T>. Signatures may be imperative (T) or reactive (Flux<T>/Mono<T>).

open as a page

What is the role of the Brave vs OpenTelemetry bridge in Micrometer Tracing, and what dependencies do you need to export spans to Zipkin?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Micrometer Tracing defines a vendor-neutral API; a bridge plugs in a real tracer — either Brave or OpenTelemetry. To send spans to Zipkin you add the bridge plus a Zipkin reporter/exporter dependency; Spring Boot auto-configures the endpoint.

open as a page

How do messaging contracts and stub distribution work, and how do you fit Spring Cloud Contract into a CI/CD pipeline?

level: principalimportance: should knowfreq 25%

basics

~20 s

Contracts also cover messaging: they describe an input trigger and an output message on a channel. The producer's build verifies it publishes the right message; consumers use StubTrigger to fire it. In CI, the producer must verify contracts and publish the stubs JAR before consumers pull it, so stub distribution and versioning must be governed.

open as a page

How does trace context survive across thread boundaries (async, thread pools, reactive) in Micrometer Tracing, and how does sampling affect what you can observe?

level: principalimportance: should knowfreq 40%

basics

~20 s

Trace context is stored per-thread, so it does not automatically follow work handed to another thread. You must propagate it — wrap executors, use context-propagation instrumentation, or capture/restore the context. Sampling decides upfront whether a whole trace is recorded, so unsampled traces produce no spans.

open as a page

As an architect, when would you standardize on Spring Cloud Function, and what are the tradeoffs versus plain controllers or Spring Cloud Stream directly?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Standardize on it when you need the same business logic to run across web, messaging, and serverless, or want to keep serverless as an option. The tradeoff is an extra abstraction and FaaS cold-start/native-image cost versus the simpler, more feature-rich plain controller model for pure web apps.

open as a page