How Spring for Apache Kafka fits Java services on your Kafka cluster
Spring for Apache Kafka is the supported Java integration layer over the Apache Kafka client, and it is the layer most Java teams will use to write producers and listeners inside Spring services. This page covers KafkaTemplate, message listeners, transactions, error handling and Micrometer observability, and it follows the order in which a team usually builds them. Your Kafka cluster does not change because of the Spring layer. Brokers, topics, listeners and security settings stay where they are, and the Spring code only configures how your services connect to them.
The Apache Kafka project is licensed under the Apache License 2.0 and has no paid edition from the project itself, so the cost of the Spring integration sits in your engineering time, your build pipeline and any partner work you buy, not in a license fee. Before you pin versions, confirm which Apache Kafka client version your Spring release depends on and check that it matches the brokers you run. The Apache Kafka downloads page lists the supported broker releases, and your Spring release notes should state the client version it bundles. Treat that pairing as a build-time decision you record in your dependency management, not something to assume from the broker version.
The Spring layer also sets the boundary of what your team owns. Authentication settings for SASL or SSL, consumer group naming, retry topics and dead letter topics must match the access controls and topic plan on the cluster, so the people who run the brokers and the people who write the listeners should agree on them before the first service goes live. Ask your platform team which mechanisms the cluster enforces and which Spring properties carry those settings. A common early risk is a service that works in a test environment with open listeners and fails in production when ACLs are enforced, so run one listener and one producer against a cluster with production-style security during the build.
What you need in place before writing the first listener
A Spring Kafka application depends on decisions made outside the code: which Kafka release runs the cluster, how clients reach and authenticate to it, and which topics and ACLs they may use. Confirm these five items with the platform team before the first @KafkaListener is written, because each one changes how the Spring configuration is built.
A running KRaft cluster
Confirm the cluster runs a supported Apache Kafka release: 4.1.2, 4.2.2 or 4.3.1 on the downloads page. KRaft is required for 4.x, so a ZooKeeper-based cluster must migrate before the application team can rely on current broker behavior. Ask which release the brokers run, since client library versions should be aligned with it and checked against the Spring Kafka compatibility notes.
Java runtime for the application
Client applications and Kafka Streams need at least Java 11 according to the Kafka upgrade notes, but the Spring release line you choose sets its own Java baseline. Check the Spring project's compatibility notes for the exact Spring Boot and Spring for Apache Kafka versions, then confirm the JDK in your build image and runtime matches before dependency resolution begins.
Network paths and TLS certificates
Every listener the application uses must be reachable from its runtime network, including bootstrap servers and each advertised broker address. Confirm which listeners are TLS-encrypted, obtain the CA chain and truststore the application will load, and verify hostnames match the certificates. Mismatched advertised listeners are a common cause of connections that bootstrap but then fail on metadata.
Service identity and credentials
Request a dedicated identity for the application rather than sharing a human or admin account. Kafka supports SASL mechanisms including SCRAM-SHA-256, SCRAM-SHA-512, GSSAPI and OAUTHBEARER, or SSL client certificates. Ask the platform team which mechanism applies, where secrets will be stored, and how rotation is handled without restarting every listener at once.
Topic and ACL plan
Agree the topic names, partition counts, retention settings and consumer group names with the platform team before the first listener subscribes. In KRaft clusters, resources without ACLs are restricted to super users unless allow.everyone.if.no.acl.found is enabled, so the service identity needs explicit read, write and group permissions. Request the ACL list in writing so a first deployment does not fail on authorization.
Where the Spring layer ends and the Kafka client begins
Spring for Apache Kafka is a configuration and programming layer over the Apache Kafka Java client, not a separate messaging system. Every guarantee that matters for production, including idempotent sends, transactions, consumer offsets and ACL enforcement, is decided by the Kafka client and the brokers. A Spring setting that enables idempotence or a transaction is only a wrapper around the same client property, so the broker behaves exactly as it would in plain Java. The practical consequence is that a team cannot learn Kafka semantics from Spring's reference material alone. Before relying on any setting, the team should read the Kafka documentation for that behavior and confirm the client version bundled with its Spring Boot or Spring Kafka release.
Four Spring components map to the parts of the Kafka client that the team will configure and test. KafkaTemplate sends records and exposes the producer settings that control acknowledgements, retries and idempotence. Listener containers, configured through @KafkaListener or a ConcurrentMessageListenerContainer, own the consumer, poll loop, concurrency and offset commits. The error handler decides what happens to a record that throws, including retries and routing to a dead letter topic, and it runs in the consumer thread. The transaction manager, such as KafkaTransactionManager, coordinates the producer transaction with the consumer offset commit for read-process-write flows. Each of these can be tested against a broker, and each has a Kafka client setting underneath it that the reader must be able to name.
Spring only expresses settings the Kafka client already honors, so any key that does not exist in the Kafka producer, consumer or admin configuration is either a Spring convenience or an error. Teams should keep a short register that maps each Spring property to its Kafka client key and to the Kafka documentation page that defines it, then review that register on every Spring Boot or Spring Kafka upgrade. Confirm during the design phase which Kafka release line your broker cluster runs and which client version your application uses, because the two must be compatible. Where a behavior is critical, such as exactly-once processing, add an integration test against your own brokers that proves the behavior rather than trusting the framework default.
Configuration sequence for a Spring Kafka service
This sequence takes a Spring Kafka service from agreed topic design to a producer, consumer, error path and metrics that have each been checked against the cluster. Each step closes with a verifiable result so the platform team and the application team can confirm progress together.
Agree topics, partitions and ACLs first
The application team and the platform team agree topic names, partition counts, retention and the access control list entries each service principal needs before any code is written. The platform team creates the topics and ACLs, or grants the application team the rights to do so, in the cluster's authorization model. Kafka's StandardAuthorizer in KRaft mode stores ACLs in the metadata log, and resources without ACLs are limited to super users unless allow.everyone.if.no.acl.found is set to true, so confirm that setting with your platform team before testing. Done means each service principal has written ACLs for its produce topics and read ACLs for its consumer group and input topics, and a denied test shows that an unlisted principal cannot write to the topic.
Configure bootstrap servers, TLS and SASL
The service sets spring.kafka.bootstrap-servers, the security protocol, the SASL mechanism and the truststore or keystore paths in application configuration, with credentials supplied from your secrets store rather than committed files. Kafka supports SSL and SASL authentication, and the mechanism must match what the brokers advertise, so the platform team should confirm the listener and mechanism for the target environment. Start the service and check the startup logs for a successful connection to the cluster with no SSL handshake or SASL authentication errors. Done means the application's AdminClient can describe the topics it is allowed to see.
Build the idempotent KafkaTemplate producer
Define the producer factory with enable.idempotence set to true and acks set so that the broker waits for full acknowledgement, then publish through KafkaTemplate with a send result callback that logs the partition and offset. Idempotence prevents duplicates caused by producer retries within a session, but it does not cover duplicate business events created by the caller, so the message key and payload design still matter. Produce one test record to a non-production topic, then read it back with kafka-console-consumer using the same credentials to confirm the value, key and headers arrived. Done means the record appears with the expected key and offset, and the producer log shows no retry warnings for that send.
Add a listener with an explicit group ID
Annotate the listener method with @KafkaListener and set groupId explicitly, along with the offset reset behavior, instead of relying on defaults that differ between teams and environments. Set the auto-commit and acknowledgment mode according to the processing guarantee the service needs, and document the choice next to the listener, since commit timing decides what happens after a crash. Confirm the consumer group in the cluster by listing its members and offsets with the consumer group tooling, and confirm the record produced in the previous step is consumed exactly once in normal operation. Done means the group appears with the expected name, its committed offset advances after processing, and the topic lag returns to zero.
Attach the error handler and dead letter topic
Configure a DefaultErrorHandler with a bounded backoff and a DeadLetterPublishingRecoverer that routes records failing after the final attempt to a dead letter topic named by the platform team. Make the dead letter topic's ACLs explicit so the service can write to it and an operations group can read it, and decide whether a poison record blocks its partition or moves to a retry topic. Send a deliberately malformed record in a test environment and confirm the listener retries, then publishes the record to the dead letter topic with the original headers and exception information. Done means the failed record appears on the dead letter topic, the main consumer offset moves past it, and the log records the final failure.
Enable transactions for read-process-write paths only
Set a transactional ID prefix and transaction manager only on the listeners or services that read from one topic, process, and write to another topic in the same unit of work. Transactions add coordination overhead and require the consumers that read the output to use read_committed, so leave ordinary produce-only and consume-only services on the idempotent producer. Grant the transactional ID its own ACL, then run a test where the output is written with the consumed offsets inside the transaction, and read the output with a read_committed consumer. Test a failed transaction too, to confirm that uncommitted records stay hidden from read_committed consumers. Done means the committed output is visible and the aborted output is not, and the team has documented which external side effects remain outside the guarantee.
Expose Micrometer metrics to the monitoring stack
Enable the Spring Boot actuator and Micrometer registry for your monitoring system, then confirm that Kafka producer, consumer and listener metrics are exported with the service's application name and environment tags. Spring Kafka registers Micrometer meters for its listener containers and templates when a MeterRegistry is present, so verify the metric names in the actuator endpoint before building dashboards. Ask the platform team which scrape or push endpoint the monitoring stack uses, and confirm that the target shows as up with recent samples. Done means consumer lag and listener throughput for the service appear on a dashboard, and an alert can be triggered by a stopped consumer in a test environment.
Choosing between blocking retries, retry topics and dead letter topics
When records for one key must be applied in sequence, such as successive balance changes on one account, a failed record should hold its partition until it succeeds or is set aside. Blocking retries keep that order because the listener retries the same record before it moves to the next one. The cost is that one bad record delays everything behind it on the same partition.
- Ordering: later records for the same key wait, so sequence holds through the retry window.
- Replay effort: little replay is needed, because failed records are retried in place and stay in their original sequence.
- Duplicates: a failure after a successful write can cause the record to be processed again, so the listener logic must be idempotent.
- Stall risk: a poison message blocks its partition until retries are exhausted or the record is routed out.
- Dead letter handoff: once retries run out, the record goes to a dead letter topic, and later records for that key can overtake it unless your team defines how to handle that gap.
Confirm the error handler class, backoff settings and retry options against the Spring Kafka reference for your release before you commit to this design.
When throughput matters more than strict progress on every partition, a poison message must not hold up healthy traffic. Non-blocking retries move a failing record to retry topics with delays, so the main topic keeps flowing while the failed record waits its turn. This design suits consumers where records for the same key can tolerate reordering.
- Throughput: the main topic keeps moving while failing records wait in retry topics.
- Ordering trade-off: once a record is parked, later records for the same key can be processed first.
- Replay effort: dead letters need a replay procedure, and the underlying defect should be fixed and verified before records are replayed.
- Duplicates: retries and republishing can deliver a record more than once, so downstream writes need a stable business key or an idempotent upsert.
- Topic sprawl: each retry step adds topics that need naming, retention, access control and monitoring.
Confirm the retry topic suffixes, attempt counts, delay behavior and dead letter settings against the Spring Kafka reference for your release, and test how your broker and access policies treat the extra topics.
When processing calls a payment provider, a partner API or another service with quotas, the retry design is driven by the downstream limit rather than by Kafka alone. Immediate retries can make throttling worse, while blocking backoff slows the whole partition. Either choice needs the consumer concurrency and the partition count planned around the external limit.
- Backoff shape: spread retries with backoff, and confirm which backoff options your release supports.
- Partition throughput: blocking backoff caps throughput per partition, so size consumer concurrency to the external rate limit.
- Retry classification: throttling and timeout responses are usually worth retrying, while validation errors should go straight to a dead letter topic.
- Duplicates: a call that succeeds but times out will be retried, so use the provider's idempotency key where one exists.
- Replay pacing: replaying a dead letter topic at full speed can trigger the same throttling again, so replay should be paced.
Confirm which exception types the error handler retries, how it treats non-retryable exceptions, and how it handles the back-pressure from your provider in the Spring Kafka reference for your release.
Mapping Kafka concepts to Spring configuration and service ownership
This table maps each Kafka concept a Spring service touches to the property that sets it and the team that should approve changes to it. Read it before a service ships, and use the approver column to keep group IDs, principals and topic names out of application code changes that no platform team has reviewed.
| Kafka concept | Spring setting or source | Who approves changes |
|---|---|---|
| Bootstrap servers and listener endpoints | spring.kafka.bootstrap-servers, set per environment through externalized configuration | Platform team, with the security team for any TLS or SASL listener change |
| Topic names and partition counts | NewTopic beans or topic provisioning outside the service, referenced by name in @KafkaListener and KafkaTemplate calls | Data owner and platform team, through the topic request process |
| Consumer group IDs | spring.kafka.consumer.group-id or the groupId attribute on @KafkaListener | Owning service team, confirmed by the platform team because a new ID resets offsets |
| Transactional ID prefix | spring.kafka.producer.transaction-id-prefix, which Spring appends to per-instance suffixes | Platform team, since prefixes must be unique across services sharing a cluster |
| ACL principals | The SASL username or certificate identity in spring.kafka.security and jaas configuration, matched to broker ACLs | Security team, with the platform team applying ACLs in the cluster |
| Offset reset policy | spring.kafka.consumer.auto-offset-reset, which applies only when a group has no committed offset | Owning service team, with review by the data owner for replay or skip decisions |
| Dead letter topic naming | DeadLetterPublishingRecoverer destination resolver or the default suffix pattern, applied in the error handler bean | Platform team, to keep naming consistent across services |
| Micrometer meter names | Observation and meter settings in Spring Boot and the Micrometer registry, including the Kafka listener and template metrics | Observability team, since dashboards and alerts depend on these names |
Security settings to make in the Spring application
Kafka security is optional and off by default, so the Spring application must carry its own encryption, authentication and authorization settings. The cards below cover the client configuration that connects to a secured cluster and the broker-side ACL decisions that determine what each service principal can reach.
Encrypt traffic with TLS truststores
Set security.protocol to SSL or SASL_SSL in the Spring Kafka producer and consumer properties, then point ssl.truststore.location and ssl.truststore.password at the CA that signed the broker certificates. Load the truststore from a mounted secret or file path, not from a file bundled in the application JAR. Confirm hostname verification is on, since a truststore alone does not stop a client from trusting the wrong broker.
Choose one documented SASL mechanism
Kafka supports GSSAPI (Kerberos), PLAIN, SCRAM-SHA-256, SCRAM-SHA-512 and OAUTHBEARER for client authentication. Pick the mechanism that matches your identity system and set sasl.mechanism and the matching JAAS login settings through spring.kafka.properties. Use SCRAM or OAUTHBEARER with short-lived credentials where your identity provider supports them, and confirm the broker enables the same mechanism on the listener the application uses.
Scope ACLs to each service principal
Grant each application principal only the topic operations it needs, such as Read and Write on its own input and output topics. Bind consumer group access separately, so a service can join only its own group.id values. Ask the platform team to confirm that the Spring service account has no cluster-level or wildcard permissions that were added for convenience during testing.
Default-deny in KRaft clusters
In KRaft mode the StandardAuthorizer stores ACLs in the cluster metadata log. Resources without ACLs are restricted to super users unless allow.everyone.if.no.acl.found is set to true, so a new topic can silently block the application until an ACL is added. Keep that setting false in production and add the ACL in the same change that creates the topic, so deployments fail visibly rather than open access.
Keep credentials out of code and logs
Store the truststore password, SASL credentials and keystore material in a secrets manager or Kubernetes secrets and inject them as environment variables or mounted files. Avoid logging the full spring.kafka property map at startup, because Spring Boot's configuration endpoints and debug logs can expose passwords. Rotate SCRAM passwords or OAuth client secrets on a schedule and test the rotation path before it is needed.
Refuse plaintext in production
Because Kafka security is optional, a broker can expose a plaintext listener that accepts clients with no encryption or authentication. Add a startup check in the Spring application that fails when security.protocol is PLAINTEXT and the active profile is production, and keep the bootstrap servers list limited to the secured listener. Verify the check with a test that starts the application against a plaintext endpoint and confirms it refuses to run.
Common Spring Kafka failures and how to diagnose them
This table lists the failures teams most often hit when running Spring Kafka services against a self-hosted cluster, with the likely cause, the check to run and the fix for each. Read it as a triage sheet: start from the symptom your team sees, confirm the cause with the check, then apply the fix in configuration or cluster policy.
| Failure | Likely cause and check | Fix |
|---|---|---|
| Authorization failures on produce or consume | The client principal has no ACL for the topic, consumer group or transactional ID. Check the ACLs for the principal in the authorizer and confirm the principal name the broker sees matches the one in the service's SASL configuration. | Grant the narrowest ACLs the service needs (Write or Read on the topic, Read on the group). Avoid relying on allow.everyone.if.no.acl.found to paper over gaps. |
| TLS handshake errors after deployment | The truststore in the service does not contain the CA that signed the broker certificate, or the keystore lacks the client certificate when mutual TLS is required. Compare the certificate chain the broker presents with the truststore contents and check hostname verification against the advertised listener name. | Rebuild the truststore with the correct CA chain, align keystore and key password settings, and make sure advertised listener hostnames appear in the broker certificate subject alternative names. |
| Consumers stop receiving after a group ID change | A new group ID has no committed offsets, so auto.offset.reset decides the starting point, often latest. Check the group's committed offsets and the reset setting, and confirm whether the old group still holds offsets for the same topic. | Set the starting position on purpose, either by resetting offsets for the new group during a planned window or by keeping the original group ID. Document the decision in the service's release notes. |
| Duplicates after retries with idempotence disabled | The producer runs without enable.idempotence, or a setting that conflicts with it silently turns it off, so a retried send can write the same record twice. Check the producer configuration at startup, including acks and retry settings, and look for duplicate keys in the downstream topic. | Enable idempotence in the producer factory, keep acks at the value idempotence requires, and handle any remaining duplicates in consumers, since idempotence covers only producer retries. |
| Transactions appear to hang for read_committed consumers | An open or aborted transaction holds back the last stable offset, so read_committed consumers wait. Check for producers that began a transaction and did not commit or abort, and look for transactional IDs stuck after an application crash. | Make sure every transactional send path ends in a commit or abort, set transaction timeouts that fit the workload, and let fencing from a restarted instance with the same transactional ID close out the old producer. |
| Dead letters accumulate without an owner | The dead letter topic has no named team, no alert on growth and no replay procedure, so failed records sit unreviewed. Check the dead letter topic's record count trend, the headers that identify the source topic and exception, and whether any consumer reads it. | Assign an owning team in the topic catalog, alert on growth, and document a replay runbook that reprocesses records after the cause is fixed. Confirm retention on dead letter topics with the platform team. |
| Metrics missing from the scrape endpoint | The Micrometer registry is not on the classpath, the actuator endpoint is not exposed or secured in a way the scraper cannot reach, or Kafka client metrics are not bound to the registry. Check the metrics endpoint directly from inside the network and confirm the Kafka metric names appear in the response. | Expose the metrics endpoint to the scraper, bind Kafka client metrics to the Micrometer registry, and add a scrape job that targets the service's management port. |
Questions teams ask before building Spring Kafka services
Does Apache Kafka have a license fee that adds to the project cost?
No. Apache Kafka is an Apache Software Foundation project released under the Apache License 2.0, and the project itself offers no paid edition. Your cost is the infrastructure you run it on, the people who build and operate it, and any commercial support contract you choose to buy from a vendor. Spring for Apache Kafka is also open source. Confirm the license of every client library and connector your services pull in, because some components in the wider ecosystem use different terms. Ask any partner to list each dependency and its license in the written scope so the cost picture is complete before build work starts.
What drives the cost of building Spring Kafka services?
Cost follows the amount of design and verification work, not the Kafka license. The main drivers are the number of services that produce or consume, how many paths need transactions, how much error-handling design the business requires, how many topics and consumer groups must be planned, and how far security integration goes, including SASL mechanisms and identity provider wiring. Testing and observability add work in proportion to the number of services. Recurring cost comes from upgrades, aligning client versions with the broker release line, and ACL changes as teams and topics change. Ask for a written scope that lists each service, each topic, each transactional flow and each integration point, so the quote can be traced to specific work.
How long does a Spring Kafka build take?
Duration depends on the same drivers as cost: service count, transactional flows, error-handling rules, security integration and the test and observability standard you require. A single service with one topic and simple retry rules is a very different job from a set of services that share exactly-once paths and must integrate with an identity provider. Timelines are agreed in the written scope, which should name the milestones, the environments available to the team and the decisions that must be made by the client, such as topic naming and retry policy. Ask for the milestones and the client dependencies together, because most schedule slips come from waiting on access, approvals or unresolved design choices.
What does the client need to provide for a Spring Kafka engagement?
The client needs to provide a working Kafka cluster or a plan to build one, access to non-production and production-like environments, the list of services and their data contracts, the topic and consumer group naming rules, and the security requirements. Identity inputs matter: the team must know which SASL mechanism the brokers accept, how service credentials are issued and rotated, and where secrets are stored. The client also needs named decision owners for retry policy, dead letter handling and ordering rules, because those choices shape the code. Ask in the scope for a RACI table that lists which tasks belong to the client and which belong to the partner.
Can our in-house team build Spring Kafka services without a partner?
Often yes, if the team already knows Java, Spring Boot and Kafka semantics well enough to reason about offsets, rebalances, idempotence and transactions. The harder parts are rarely the annotations. They are the design choices: which paths need exactly-once, how poison messages are isolated, how ordering is preserved across retries, and how the services behave during broker upgrades. Teams without recent Kafka production experience often underestimate testing with failures and the observability needed to find duplicates or lag. A useful check is whether your engineers can explain, for each consumer, what happens after a crash at each point in processing. If that answer is uncertain, a scoped design review is a lower-risk first step than a full build.
What work recurs after a Spring Kafka service goes live?
Recurring work includes upgrading the Kafka brokers and the clients on a planned cadence, aligning Spring Kafka and the Kafka client library versions with the broker release line, and reviewing ACLs when teams, topics or service accounts change. Rolling upgrades are supported from each release in the past year to the latest version, according to the Kafka time-based release plan, so the client and broker upgrade path should be scheduled rather than improvised. Dead letter topics also need an owner who replays or discards messages. Ask for a written list of recurring tasks, their owner and the trigger for each, such as a new supported release or a change in topic ownership.
How do Spring Kafka services connect to SASL and identity systems?
Kafka authenticates clients with SSL or SASL, and the documented SASL mechanisms are GSSAPI (Kerberos), PLAIN, SCRAM-SHA-256, SCRAM-SHA-512 and OAUTHBEARER. Spring Kafka passes these settings through its client configuration, so the work is choosing the mechanism the brokers enforce, storing credentials in a secrets system, and authorizing each service with ACLs. Authorization in KRaft clusters is stored in the metadata log, and resources without ACLs are restricted to super users unless allow.everyone.if.no.acl.found is set to true. Confirm with your security team which mechanism and identity provider apply before writing configuration, and treat that setting as a deliberate decision rather than a default.
What testing and observability should a Spring Kafka scope include?
At minimum, the scope should cover tests that run against a real broker, including failure cases such as retries, dead letter routing, consumer restarts and transaction aborts. Verification should show that duplicates are bounded by the design, that poison messages do not block partitions, and that consumer lag is visible. Observability should expose Micrometer metrics for listeners and templates, trace context across services, and alerts on lag and dead letter volume. Ask the partner to name the test environments, the failure scenarios covered and the dashboards delivered. DeployOpen runs security testing of applications and APIs as a separate service, which can be scoped alongside the build if your organization needs it.
How DeployOpen scopes and delivers Spring Kafka services
DeployOpen approaches Spring for Apache Kafka work as a planned application delivery, starting from a written scope that names the topics, listeners, transactions and error handling your services need. We build against your own Kafka cluster in infrastructure you control, then test, document and hand over the result so your team can run it. Engagements are fixed-scope, with ongoing support available as a retainer.
How we scope
- Inventory the producers, listeners and topics each Spring service will use, with owners named for each
- Agree which delivery guarantees each flow needs, such as idempotent sends, transactions or at-least-once handling
- Define retry, backoff and dead letter topic behavior per consumer group before any code is written
- Record the written scope, deliverables and timeline in writing before work starts
How we build
- Configure KafkaTemplate, listener containers and serializers against the topic and schema design agreed in scope
- Implement error handlers, non-blocking retry topics and dead letter topics with the retry policy documented
- Wire Micrometer metrics and tracing into the services so consumer lag and error rates are visible to operators
- Test listener behavior, transaction boundaries and redelivery paths in a non-production environment that mirrors yours
How we hand over
- Deliver source code, configuration and a runbook covering topic names, group IDs and dead letter replay steps
- Document the Spring Kafka and Kafka client versions used, and the broker versions they were tested against
- Walk your engineers through the transaction, retry and observability design so they can change it safely
- Provide a checklist of items your team should confirm with its Kafka platform owners before production
How we support
- Offer ongoing support, dependency upgrades and regression testing as a retainer after handover
- Review Spring Kafka and Kafka client upgrades against the broker versions your cluster runs
- Re-run listener and retry tests when topics, schemas or consumer group designs change
- Scope any follow-on work in writing so changes stay traceable to a documented request

