Streaming engineering is correctness over time
A demonstration that produces one message and prints it from a consumer proves connectivity. It does not prove streaming engineering. A durable system must explain where an event goes when a broker fails after receiving it but before the producer sees an acknowledgment. It must explain what happens when a consumer writes to a database and crashes before committing its offset. It must preserve old data when the schema changes, restore processor state after failure, detect a stalled partition, constrain identities, and replay history without sending a notification twice.
Apache Kafka provides a distributed, replicated, partitioned log. Producers append records. Consumers fetch from positions called offsets. Retention separates consumption from deletion so several consumer groups can process the same history independently and at different speeds. That model creates powerful recovery and integration patterns, but it also makes keys, partitions, retention, commits, and schemas long-lived contracts.
The five-phase Kafka streaming roadmap develops those contracts in order. The first phase covers records, keys, partitions, replication, retention, and compaction. The second addresses producers, consumer groups, offsets, retries, transactions, and external idempotency. The third adds schemas and stateful processing. The fourth secures and observes the platform. The final phase turns retention and replication into tested replay and disaster recovery.
Keys and partitions are application contracts
Kafka orders records within a partition. It does not promise a single total order across all partitions in a topic. If all events for one order must be processed in order, use a stable representation of the order identifier as the key. Records with that key should select the same partition under the agreed partitioner and partition count.
This means partitioning is not only an infrastructure choice. The key serializer, partitioner, partition count, and key distribution affect correctness and scaling. Increasing partitions may create more consumer parallelism, but existing and new records for a key can map differently under some partitioning changes. A migration must account for that rather than treating a partition increase as a transparent capacity toggle.
Partition count also bounds useful parallelism within one consumer group. A group with eight assigned partitions can use at most eight active partition owners at one moment. More consumers can provide standby capacity but cannot make those eight partitions process in parallel beyond eight owners. Fewer partitions may constrain throughput; too many add metadata, file handles, leader elections, replication work, and small-segment overhead.
Key distribution matters as much as count. A single very active customer, device, or tenant can dominate one partition while others remain quiet. Measure records and bytes by partition and inspect processing time and lag by partition. If ordering requirements allow it, a composite or sharded key may spread load. If they do not, the hot aggregate requires an application-level redesign rather than random partitioning that breaks order.
Replication, acknowledgment, retention, and compaction
Each Kafka partition has a leader and follower replicas. The leader normally handles producer and consumer requests while followers copy its log. Replication factor determines how many replicas exist. The set of in-sync replicas reflects replicas sufficiently caught up under current rules. Producer acknowledgments and minimum in-sync replica policy determine when a write is accepted.
For a critical topic, a common direction is acks=all with a deliberate min.insync.replicas and enough replicas to tolerate the intended failures. The exact numbers are deployment decisions, not universal constants. A stricter policy can reject writes when replicas are unavailable, protecting durability at the cost of write availability. A weaker policy can keep accepting writes with less redundancy. Test broker loss and network delay to verify the actual result.
Retention controls how long old log segments remain, independent of one consumer reading them. Time and size limits define the replay window. If a projection may need rebuilding from seven days of events, topic retention, disk capacity, and any upstream archive must support at least that requirement plus operational margin. Consumer offsets cannot recover data that has already expired.
Log compaction is different. It permits cleanup of superseded values while retaining at least the latest value for each key under the compaction model. A keyed null value is a tombstone that represents deletion. Compaction is valuable for latest-state topics, changelogs, and bootstrapable tables. It is asynchronous, does not create topic-wide transactions, and should not be described as immediate privacy erasure. Keys, tombstone retention, downstream handling, and backup policy still matter.
Reliable producers: retries without invented guarantees
A producer sends records in batches to partition leaders. Configuration influences batching delay, compression, request size, delivery timeout, retry behavior, in-flight requests, acknowledgments, and idempotence. These settings interact. Optimize from measurements and failure goals rather than copying a high-throughput configuration that changes durability or memory behavior.
Network failures are uncertain. A broker may have appended a record even if the acknowledgment never reaches the producer. Retrying can therefore duplicate the append unless the producer and broker coordinate. Kafka's idempotent producer uses producer identity and sequence information to suppress duplicates caused by eligible retries and preserve compatible ordering. Current clients may enable idempotence by default when configurations do not conflict, but engineers should verify the exact client documentation and effective settings.
Producer idempotence is not business idempotency. If an API receives the same order command twice and intentionally calls send twice, those are two application sends. Kafka cannot infer that their payloads represent one business action. Put a durable event or command identity in the envelope, define how the domain handles duplicates, and make consumers idempotent.
Handle send results explicitly. A callback or future should record safe topic, partition, offset, latency, and error classification. Do not log sensitive payloads or credentials. Separate authorization and serialization failures from retriable transport conditions. Bound retry and delivery time so a failing dependency does not consume unbounded memory or hide a prolonged outage.
Consumers, offsets, rebalances, and side effects
A consumer group load-balances partitions among its members. Different groups receive the same topic independently and maintain independent committed offsets. Members join a coordinator, receive assignments, heartbeat, poll, process, and commit positions. When membership or subscribed partitions change, the group rebalances.
The committed offset is a recovery position, not proof of business completion. Suppose a consumer commits offset 101 and then tries to write record 100 to a projection database. If it crashes before the database write, another member can resume after the committed position and never perform the missing write. Reversing the order—write then commit—creates at-least-once behavior: a crash after the write but before the commit can process the record again.
At-least-once is practical when the external operation is idempotent. Use a stable event ID and store it with the projection update in one database transaction where possible. A repeated event then becomes an upsert or no-op rather than a second shipment, notification, or charge-like action. Commit only after durable completion.
Long processing interacts with group liveness. In the Java consumer, calls to poll must remain within max.poll.interval.ms. If processing a batch exceeds that interval, the member may lose its assignment. Bound records per poll, decouple processing carefully, pause partitions when queues fill, and preserve partition order and commit safety. Timeout inflation alone can make real failure detection slow.
Rebalance callbacks are correctness boundaries. Stop accepting new work for revoked partitions, complete or cancel outstanding work according to policy, and commit only completed contiguous positions while ownership is valid. Cooperative assignment can reduce movement in compatible client groups, while static membership can reduce avoidable reassignment during short restarts. Verify current protocol guidance because Kafka client group behavior evolves.
Exactly-once is a scoped system property
Kafka transactions can atomically commit produced records and consumed offsets. In a consume-transform-produce application, a transactional producer begins a transaction, writes output records, sends the consumed offsets to the transaction, and commits or aborts. Downstream consumers configured with read_committed avoid aborted transactional records. A stable transactional.id supports fencing of obsolete producer instances.
This provides a strong Kafka-to-Kafka boundary. It does not automatically include a database update, email, HTTP call, or other external effect. If a transaction commits to Kafka but an external notification is uncertain, the overall workflow is not exactly once. Use an outbox, inbox, durable deduplication, sink transaction support, or another explicit coordination pattern.
Apache Flink can provide consistent managed-state recovery using checkpoints and rewindable Kafka sources. End-to-end semantics still depend on the sink. An exactly-once-capable transactional sink has a different contract from an arbitrary REST endpoint. State the scope precisely: “Kafka output and offsets commit atomically” is more useful than “the platform is exactly once.”
Schema evolution must account for retained history
Events outlive a single deployment. A schema registry gives each schema version an identity and can enforce compatibility under a subject. Confluent Schema Registry supports Avro, Protobuf, and JSON Schema, but their compatibility details differ. Use the documentation for the actual format rather than applying one Avro rule to every payload type.
Backward compatibility means a consumer using the new schema can read data written with the previous schema. In Avro, adding a field with an appropriate default is a classic backward-compatible change because old records do not contain the field and the reader needs a value. Forward compatibility asks whether an older reader can process new data. Full compatibility combines both directions.
Non-transitive modes compare the new schema with the latest prior version. Transitive modes compare it with all registered history. Long-retained topics and full replays often justify a transitive policy, but the strongest policy is not automatically correct for every team. It can constrain evolution and requires governance of subject scope.
Compatibility does not validate business meaning. Changing a field from “gross cents” to “net cents” may remain syntactically compatible while breaking every aggregate. Contract tests should deserialize representative old records, validate invariants, and exercise mixed producer and consumer versions. For an intentionally incompatible redesign, create a new topic and schema and run a controlled migration instead of setting compatibility to NONE casually.
Kafka Streams and Flink place state at different boundaries
Kafka Streams is a client library embedded in an application. It maps input partitions to tasks, maintains local state stores, backs durable state with changelog topics, and creates repartition topics when keyed operations require redistribution. Its deployment unit is the application instance. Stable application identity and compatible state and schema evolution are central to recovery.
Apache Flink runs a distributed dataflow with managed operator state. Keyed state is colocated with keyed stream partitions and organized into key groups that can be redistributed when parallelism changes. This supports windows, joins, pattern detection, timers, and other stateful operations.
Flink checkpoints capture a consistent snapshot of operator state and corresponding source positions. Checkpoint barriers flow through the topology; recovery restores a completed snapshot and resets Kafka sources to recorded positions. The interval trades steady-state overhead against the amount of replay and recovery time. Durable file-system or object-store-backed checkpoint storage is appropriate for high-availability setups; JobManager heap storage is mainly for small local state.
Savepoints are user-managed consistent state images for stop-resume, upgrade, fork, or rescale operations. Assign stable operator UIDs. Auto-generated IDs depend on program topology and can change after edits, making old state difficult or impossible to map. Test savepoint restore before the upgrade window and understand snapshot file ownership and disposal.
Secure every identity and resource operation
A Kafka security boundary has three core layers. TLS protects transport and validates server identity. Authentication establishes the client principal. Authorization decides what that principal can do to topics, consumer groups, transactional IDs, cluster operations, and other resources.
Issue distinct identities for producers, consumers, stream processors, replication connectors, schema clients, and operators. An order producer needs write access to specific topics, not wildcard read or cluster administration. A projection consumer needs read access to its input and access to its group identifiers. A transactional processor may also need its transactional-ID scope and internal topics.
Negative tests matter. Attempt to read an unrelated topic, write to a restricted topic, join another team's group, register an incompatible schema, and perform an administrative action. Each should fail under the intended principal. Keeping an administrator credential in an application image defeats the boundary even if ACL definitions look correct.
Keep credentials out of source code, images, event headers, traces, logs, and support bundles. Preserve TLS certificate verification. Plan rotation and expiry alerts. Restrict broker and diagnostic listeners by network policy or firewall. Use quotas to contain a compromised or misconfigured client that can otherwise exhaust bandwidth, requests, or storage.
Observe event age and processing, not only broker uptime
A green broker process does not prove that shipments are current. A consumer may have stopped polling, one hot partition may be hours behind, a schema failure may block processing, or producers may have silently stopped. Operability requires broker, client, processor, and business-freshness signals.
Broker signals include request rates and latency, failed requests, under-replicated or unavailable partitions, in-sync replica changes, controller health, disk capacity, network throughput, and throttling. Producer signals include record and byte rate, batches, compression, retries, errors, request latency, delivery age, and throttle time. Consumer signals include assignments, rebalances, fetch rate, commit success, processing latency, queue depth, lag, and time since the newest successfully processed event.
Lag is useful but incomplete. It is an offset difference, not a universal time delay. Ten records can represent milliseconds or hours depending on arrival rate and event timestamps. A stalled producer can leave lag at zero while users receive no new updates. Pair lag by partition with event age, commit age, traffic, errors, processing throughput, and an external synthetic event.
OpenTelemetry messaging conventions model create, send, receive, process, and settle operations. Message creation context enables producer-consumer correlation. Batches and fan-out require span links because one span has only one parent but may represent several messages. Use low-cardinality destination and operation attributes, safe error types, and message counts. Do not put full payloads, authorization headers, customer identifiers, or random event IDs into metric labels.
Messaging semantic conventions continue to evolve, so check their stability status and migration guidance. Pin the emitted convention version or library behavior in tests. A trace should help explain a synthetic order across API, produce, consume, process, and projection operations without becoming a second copy of the event payload.
Poison records and replay need policy, not an infinite retry loop
A transient database timeout may succeed on retry. A permanently incompatible schema or invalid required field will not. Retrying permanent failures forever can block a partition and expand lag. Silently skipping every exception hides data loss. A useful error policy classifies failures and sets bounded retry, backoff, pause, alert, stop, quarantine, or skip behavior.
A governed quarantine topic can preserve the original topic, partition, offset, event ID, schema identity, attempt count, first and last failure times, and a safe bounded reason. Access should be more restrictive than ordinary business topics. Avoid copying sensitive payloads into logs or unrestricted metadata. Preserve enough information to retrieve the original retained event if policy allows.
Replay should use a dedicated consumer group and exact source positions. Suppress non-replayable notifications or direct output into an isolated projection. Stable event IDs and idempotent upserts make repeated runs safe. Rate limits prevent a backfill from starving live consumers or saturating downstream databases.
Reconciliation is the release gate. Count source records, unique event IDs, quarantined records, duplicates, accepted terminal states, and output rows. Compare checksums or business invariants. Do not switch production reads to a rebuilt projection merely because the replay process exited successfully.
Multi-cluster recovery is more than topic replication
Apache Kafka MirrorMaker 2 is built on Kafka Connect and supports replication flows between clusters, including heartbeat and checkpoint-related capabilities. A recovery design should use an explicit topic allowlist, clear replicated topic naming, loop prevention, least-privilege connector identities, destination topic configuration, and monitoring.
Replication is typically asynchronous, so recovery point objective depends on measured lag and failure timing. Consumer recovery depends on checkpoint and offset-sync information, active group behavior, and the exact topology. Validate translated positions against event IDs and timestamps rather than assuming equal numeric offsets across clusters.
Schemas need a separate plan. Schema IDs are scoped to a registry. Copying Kafka records without ensuring the destination can resolve the encoded schema can leave intact bytes that no recovery consumer can deserialize. Test old and new schema versions in the recovery environment before an incident.
Processor state also matters. A Flink job's Kafka input is only one part of its state. Windows, timers, aggregates, and sink transaction state live in checkpoints or savepoints. Store them durably, keep stable operator UIDs, and test restore with recovery cluster endpoints and security configuration.
For active-passive recovery, prevent split brain. Freeze or fence primary producers, capture final known positions and replication state, validate the standby, then move a bounded producer and consumer cohort. Measure time to first accepted write, first correct read, restored state, and projection freshness. Record duplicates and gaps rather than claiming zero loss.
Failback is another migration. After the original environment is repaired, decide how recovery-era events flow back, fence the current writer, synchronize data and schemas, validate positions and state, move bounded clients, and reconcile again. Simply flipping a DNS record back can create divergent logs and repeated side effects.
Test contracts and crash points systematically
Unit tests should cover serialization, key derivation, idempotency decisions, state transitions, and error classification. Contract tests should register compatible and incompatible schemas and decode retained versions. Integration tests should use a disposable broker and real client behavior for produce, poll, commit, transactions, compaction, tombstones, ACLs, and rebalances.
Failure injection should target uncertainty boundaries: broker receives a record but producer times out; consumer writes output then crashes; consumer crashes before output; partition ownership changes with work in flight; schema registry is unavailable; one key creates skew; disk fills; a Flink task fails during checkpoint; MirrorMaker destination is down; a restore uses a changed operator UID; replay runs twice.
Load testing should vary record rate, payload size, key skew, batch size, compression, partition count, consumer processing time, state size, and destination latency independently. Measure p50 and p95 producer latency, throughput, retries, throttle time, lag, event age, rebalance duration, state growth, checkpoint duration, restore time, and dropped or quarantined events.
Publish a capacity envelope rather than a universal throughput claim. State the hardware, versions, partitions, replication, payloads, key distribution, durability settings, processor topology, and failure conditions. A benchmark without configuration and failure context is not reusable engineering evidence.
Cost follows bytes, replicas, state, and time
Streaming cost is not only broker compute. Retained payload bytes are multiplied by replication. More partitions add metadata and small-file overhead. Cross-cluster replication adds storage and transfer. Kafka Streams and Flink add CPU, memory, local state, changelog or checkpoint storage, and recovery traffic. Schema systems, projection databases, traces, metrics, logs, snapshots, and test environments add their own retention.
Attribute bytes and records by topic and producer, lag and processing by group, state and checkpoints by job, and telemetry by service and signal. Compression may reduce network and storage at CPU cost. Longer batching may improve throughput at latency cost. Longer retention improves replay at storage cost. More replicas improve failure tolerance at storage and network cost.
Cleanup is part of the cost design. Stop producers and replay jobs first. Export only sanitized approved evidence. Remove processors, topics, schemas, checkpoints, savepoints, quarantine and replay data, projection records, credentials, certificates, telemetry, images, volumes, networks, and disposable clusters. Verify no process listens and no billable or accessible resource remains.
Two projects that demonstrate practical skill
Event-driven order platform with schemas and idempotency
The first Kafka streaming project uses synthetic order, inventory, payment, fulfillment, and status events. A stable order_id key preserves per-order partition ordering. A registry enforces a tested schema policy. Producers use durable acknowledgments and idempotence. Consumers persist event-ID deduplication with projections before committing offsets.
A Kafka Streams or Flink job aggregates outcomes into a compacted latest-status topic. Distinct workload identities receive least-privilege topic, group, transaction, and schema access. OpenTelemetry links asynchronous work, while dashboards combine broker, producer, consumer, state, lag, and event-age signals. Permanent failures enter a restricted quarantine path; a dedicated replay group rebuilds the projection and reconciles every event. The project ends with load evidence, cost attribution, and teardown.
Resilient multi-cluster streaming with observability, replay, and DR
The second project runs primary and recovery Kafka clusters. MirrorMaker 2 replicates a deliberate allowlist and exposes heartbeat, checkpoint, task, and lag evidence. The recovery registry decodes all retained schema versions. Consumer positions are tested against event IDs and timestamps. A stateful Flink projection restores from durable state with stable operator UIDs.
The failover runbook fences primary writers, validates the standby, redirects a bounded client cohort, restores the processor, and measures actual RPO and RTO. An isolated replay resolves the uncertain window through idempotent output and reconciliation. Controlled failback repeats the authority, synchronization, validation, and reconciliation steps. The learner reports duplicate infrastructure and transfer cost, then destroys both environments and verifies no credential, data, snapshot, listener, or telemetry remains.
An eight-week implementation plan
- Week 1: Run a disposable Kafka cluster. Create keyed and unkeyed records, inspect partitions and offsets, test replica failure, compare time retention and compaction, and measure key skew.
- Week 2: Build reliable producers. Test acknowledgments, idempotence, batching, compression, retries, callbacks, authorization failure, broker restart, and uncertain timeout behavior.
- Week 3: Build consumer groups and idempotent projections. Test commits, crashes at each side-effect boundary, poll timing, rebalances, pause and resume, reset policy, and duplicate delivery.
- Week 4: Introduce schema registration and compatibility tests. Evolve one schema safely, reject an unsafe change, decode retained history, and document subject and deployment strategy.
- Week 5: Build keyed state with Kafka Streams or Flink. Exercise repartitioning, changelog or checkpoint recovery, late and duplicate events, savepoints, stable UIDs, rescaling, and state validation.
- Week 6: Add TLS, workload identities, ACLs, negative tests, quotas, OpenTelemetry messaging correlation, client and broker dashboards, event-age alerts, and missing-data detection.
- Week 7: Implement quarantine and replay. Classify errors, preserve source coordinates, suppress irreversible effects, rate-limit backfill, run twice, and reconcile exact event outcomes.
- Week 8: Build active-passive replication, validate schemas and offsets, restore Flink state, fail over, fail back, complete 25 original knowledge checks, attribute cost, and clean up.
Common Kafka streaming mistakes
- Assuming topic-wide order. Ordering is per partition; use a stable key for the required domain boundary.
- Adding partitions without a migration review. Key mapping and order assumptions may change.
- Calling producer idempotence end-to-end exactly once. Separate retry deduplication from business commands and external effects.
- Committing before durable processing. A crash can skip the result.
- Using event IDs as metric labels. Unbounded cardinality can overwhelm telemetry systems.
- Disabling schema compatibility during pressure. Use a new topic and explicit migration for an incompatible contract.
- Retrying poison records forever. Classify, bound, preserve evidence, quarantine, and resolve.
- Watching only aggregate lag. Inspect partitions, traffic, event age, processing, assignments, rebalances, and freshness.
- Giving applications broad wildcard ACLs. Use distinct principals and negative authorization tests.
- Copying records without schemas. Destination consumers may be unable to deserialize intact data.
- Replicating topics and calling DR complete. Offsets, processors, state, ACLs, identities, dependencies, and failback remain.
- Replaying through live irreversible effects. Isolate output, preserve event identity, and reconcile before cutover.
Present practical evidence honestly
A strong portfolio can use disposable local clusters. Publish a sanitized event catalog, key and partition decision, topic settings, schema compatibility report, retry and crash matrix, idempotency table, state topology, checkpoint or changelog recovery evidence, ACL matrix, negative tests, trace example, lag and event-age dashboard, replay reconciliation, failover timeline, RPO/RTO result, cost model, and cleanup checklist.
State limitations. A laptop cluster does not prove production scale. A synthetic payment outcome is not a real payment system. One failover test does not prove continuous availability. The work can still demonstrate disciplined reasoning about durable contracts, uncertainty, state, authorization, observability, replay, and recovery.
Roles that use these skills include streaming engineer, data engineer, platform engineer, backend engineer, distributed systems engineer, SRE, and data infrastructure engineer. Explore related data, cloud, and platform roles, but completion is not a job guarantee. Hiring also depends on programming, data modeling, systems knowledge, communication, and operational experience.
Official sources
- Apache Kafka documentation
- Kafka concepts and terms
- Kafka replication
- Kafka log compaction
- Kafka producer configuration
- Kafka consumer configuration
- Kafka delivery semantics
- Kafka Streams documentation
- Kafka security
- Kafka monitoring
- Kafka geo-replication and MirrorMaker 2
- Confluent Kafka producer guide
- Confluent Kafka consumer guide
- Confluent schema evolution and compatibility
- Apache Flink stateful stream processing
- Apache Flink checkpoints
- Apache Flink savepoints
- OpenTelemetry messaging semantic conventions
- OpenTelemetry messaging spans
Continue the practical path
- Kafka Streaming Engineering five-phase roadmap
- Kafka streaming original knowledge checks
- Kafka streaming flashcards
- Kafka streaming hands-on projects
- Data, cloud, and platform engineering jobs
- Data Engineer Tech Stack in 2026
- DevOps Engineer Tech Stack in 2026
- Cloud Lab Cost Control
- PrepKloud editorial policy
Frequently asked questions
Is Kafka streaming engineering a certification?
No. This is an independent practical path with original knowledge checks and projects. It is not an exam, credential, passing-score program, or guarantee.
Does Kafka guarantee exactly-once processing?
Kafka supports idempotent production and transactions for Kafka records and offsets. Flink supports consistent managed-state recovery. End-to-end behavior still depends on client configuration, processor mode, source, sink, external side effects, and failure handling.
How should schemas evolve in a retained event log?
Choose a format-specific compatibility policy, test every proposed schema, preserve reader support for retained history, coordinate producer and consumer deployment order, and use a new topic plus migration for intentionally incompatible contracts.
What is the safest way to replay Kafka events?
Use a dedicated replay group, exact source positions, stable event IDs, idempotent outputs, suppressed irreversible side effects, rate limits, and event-level reconciliation before cutover. Retention must still contain the required history.
What proves practical Kafka streaming skill?
Strong evidence includes tested key and schema contracts, retry and crash behavior, idempotent outputs, state restore, least-privilege ACLs, event-age telemetry, safe replay, measured failover and failback, cost attribution, and verified cleanup.