A useful Kafka topic example is a production specification, not a hello-world name. It answers four questions at once: the exact creation command with real properties, the same topic defined as code, the naming convention that scales past one team, and the schema that governs what goes into it. A realistic system needs many of them: one demonstration environment on this pattern runs 14 topics, from products for canonical state to order-events and inventory-events. This page is that specification; the topic vs partition page explains the mechanics underneath it, and the hub holds the rest.
Production-ready CLI command and properties
The production creation command carries its durability with it:
kafka-topics.sh --create --topic prod.payment.order-created.v1 \
--partitions 12 --replication-factor 3 \
--config min.insync.replicas=2 \
--config retention.ms=604800000 \
--config retention.bytes=53687091200 \
--bootstrap-server broker:9092
Each property has a job here. Replication factor 3 with min.insync.replicas=2 and producers writing acks=all is the standard production configuration, giving strong durability: every write lands on at least two replicas before it is acknowledged, and the baseline cluster shape underneath it is three brokers, one per availability zone. Cloudflare’s clusters hold a minimum replication factor of 3 across the fleet, which is what the setting looks like as policy rather than per-topic choice.
Retention is set twice on purpose. The default 7-day time retention is not protection on a high-throughput topic; retention.bytes caps the on-disk size so the topic cannot exhaust broker disk, even where that deletes data earlier than intended.
The CLI carries all of this, and it is also the honest place to say the CLI does not scale as process: shipped tooling is sufficient for a handful of topics on one cluster and becomes a friction point when multiple teams share a platform. The provisioning rate at scale makes the point: DoorDash provisions roughly 100 new topics per week, which nobody does by hand-typed commands.
One default deserves a warning label on this exact command. Since Kafka 3.0, acks defaults to all under KIP-679, and that reads as safety while quietly depending on min.insync.replicas: acks=all against a topic whose min ISR is 1 waits for exactly one replica, which is a durability gap wearing a durability setting’s name. The example’s min.insync.replicas=2 is not decoration, it is the half of the contract the producer default cannot supply.
The properties also have a bill, and it is worth computing before the topic exists rather than after. At 1 GB/s of ingest with replication factor 3 and 7-day retention, the minimum is 1.8 TB of raw disk per broker before segment overhead. Run that arithmetic for your real throughput when you set retention.bytes, because the retention setting is a disk purchase written in milliseconds. And the reason this page insists on specification over improvisation: one published case study follows a foodservice distributor whose Kafka adoption grew to a dozen product teams and roughly 130 developers, past the point where open-source tooling and CLI scripts could keep pace. Topic standards are what survive that growth. Habits do not.
Infrastructure as code definitions
Past a certain scale, topics are code. The fully realised version is Grab’s platform, where every Kafka topic, cluster, Connect connector and pipeline is declared as Terraform, and Block used open-source Kafka Terraform providers for provisioning topics and ACLs. The general principle extends past topics: Kafka configuration changes should be treated like code, version-controlled and applied through CI, because a config change applied by hand is a config change nobody can find during the incident it causes.
The two standard shapes are a Terraform kafka_topic resource block carrying the same properties as the CLI command above, and, on Kubernetes, a Strimzi KafkaTopic custom resource in YAML, where the operator reconciles the declared spec against the cluster. Application code then refers to topics as constants, KafkaTopics.INVENTORY_EVENTS rather than a string literal, so a rename is one change instead of a grep.
The payoff is recoverability as much as tidiness: managing topic configuration through code means the whole structure can be restored after a crash, and scaled across an organisation without every team reinventing conventions.
The IaC habit is old enough to predate the tools people assume it needs: Reddit ran Kafka and ZooKeeper provisioning through Terraform from 2017, pre-Kubernetes, and Shopify managed clusters across data centre regions with Chef from 2014. The tool is incidental, and the discipline is the point.
But easy creation cuts both ways, and this is a failure mode I now warn about by name: GitOps and cloud vendors have made spinning up a Kafka cluster so easy that some organisations prematurely scale by giving every team or service its own cluster, and wake up owning a proliferation of clusters, each with its own topics, versions and half-applied standards. Declare topics as code freely, and clusters as code carefully.
Two practical notes from the trenches for the Strimzi path. The 20-hours-versus-under-an-hour comparison is real: one practitioner documented spending roughly 20 hours fighting a console’s Kubernetes deployment before switching to Kafka with Strimzi and completing the equivalent setup in under an hour, so operator quality is a selection criterion, not a detail. And when your freshly declared topic returns cryptic “unknown topic” errors despite existing, check ACLs before DNS: a principal missing the DESCRIBE permission gets exactly that error, and it is a common troubleshooting dead end.
Enterprise naming conventions
The pattern that survives growth is hierarchical: [environment].[domain or event type].[dataset].[version], read left to right from broadest to narrowest. Worked: prod.payment.order-created.v1. The environment prefix separates dev, staging and prod on shared clusters. Segment two carries the domain or the event class, and one production telco’s live convention makes that segment do real work, classifying every topic as application, CDC, domain or integration before naming the dataset (CRM, billing, network) and finally the data itself. A trailing version is what lets a breaking schema change ship as v2 alongside v1 instead of on top of it.
Names also encode roles within a domain. A realistic inventory system’s topic list reads as documentation: products for canonical state, product-updates for real-time changes, order-events for deductions, inventory-events as the audit log, inventory-alerts for notifications. Derived topics inherit the parent’s name plus a suffix, and the dead-letter convention of topic name + consumer name + .dlq carries the extra fact that failures belong to a specific consumer, not to the topic. Uber’s tiered retry topics, payments.retry-1, payments.retry-2, show the same idea as levels.
The convention is worth deciding early because it ends up load-bearing: schema strategies commonly key off topic names, and organisations often scale clusters along governance or domain boundaries, which only works when the names carry the domain.
An honest confession that doubles as the argument for written conventions: my own DLQ design document got flagged in internal review for inconsistent example naming, orders.retry in one snippet, payments.notifier.retry in another, two shapes for the same concept, in a document about the concept, written by someone who thinks about this professionally. Naming conventions do not drift because people are careless. They drift because consistency has no natural enforcement point, which is why the convention has to be written down and checked mechanically, at topic creation, not culturally.
One correction to a tempting extension of the naming logic: names should carry the domain, and clusters should not be sliced by it. Scaling clusters along governance or domain boundaries is inefficient, because governance domains change more often than infrastructure should. Payments merges with billing, teams reorganise, and a cluster boundary drawn on last year’s org chart becomes this year’s migration project. Let the name carry the domain, let the cluster carry the load. By 2023 DoorDash ran 2,500+ topics across just five clusters at six billion messages a day, which is roughly the right ratio of naming structure to cluster count.
Advanced property configurations
Past the standard durability set, topic properties are chosen per use case, and three recipes cover most of them.
Changelog topics compact. cleanup.policy=compact keeps only the newest record per key, with the background log cleaner comparing dirty segments against clean ones, which is exactly the semantics a database changelog or state topic wants: current value per key, forever, without unbounded growth.
Ordered streams constrain routing. Order is guaranteed within a partition only, so strict ordering means either keyed routing, where one key’s records share one partition, or the blunt version: The New York Times runs a single-partition topic where total causal ordering across all events is required and throughput is low enough for one partition to carry it.
Throughput topics tune the batch path. compression.type=lz4 with sensible batching is the biggest tuning win available without architecture changes, and message size is part of the recipe: the 1 KB to 10 KB range is where Kafka’s batching, compression and zero-copy I/O work most efficiently together. Oversized payloads belong in object storage with a reference in the record, not in the topic.
Compression choice is worth benchmarking on your real payloads rather than inheriting from a recipe, including this page’s. The lz4 default is right most of the time, and Cloudflare measured snappy giving a 2.25x ingress reduction on their highest-throughput topic with no increase in producer or consumer CPU. The point is not snappy over lz4, it is that they measured, on the topic that mattered, and took the free win.
For the compacted-topic recipe, add the alert that catches its one silent failure: NoKeyCompactedTopicRecordsPerSec above zero. A record without a key on a compacted topic cannot be compacted, and depending on version it is either rejected or quietly accumulates outside the compaction contract. Either way it means a producer is writing to your state topic without understanding what the topic is, and that is worth a page, not a graph you check monthly.
Schema registry integration
A topic example is incomplete without its payload contract. The recommended data format for production topics is Avro or Protobuf with a Schema Registry: the registry manages schemas per topic, ensures data consistency and enables schema evolution, checking every proposed change for compatibility before a producer can ship it.
The registry keys schemas to topics by convention, most commonly the topic-name strategy, where the subject is the topic name plus -key or -value, which is one more reason the naming convention above matters: the schema layer inherits it. Enterprises run this as standard practice, with Adidas using Schema Registry for schema management and enforcement across their Kafka topics.
The contract also pays off beyond safety. Registered Avro schemas make column-level lineage achievable for Kafka data, and decoded, human-readable payloads are what make a topic browsable at all: subjects, schema versions and compatibility settings become inspectable alongside the topic they govern.
Cloudflare’s sequencing lesson is the one to steal: invest in schema governance before scaling, not after. Their early JSON usage produced exactly the tight coupling the registry exists to prevent, and the fix, migrating to Protobuf with a strict one-type-per-topic rule and a central registry, cost far more applied to a running estate than it would have cost as a day-one rule. The one-type-per-topic rule is worth adopting verbatim: it keeps every topic’s contract singular, which keeps evolution decidable. The scale ceiling for the disciplined version is genuinely high, with one global retailer running 4,000 to 5,000 tenants across its clusters on Avro.
A compatibility caution for anyone on a cloud registry: “supports the Confluent API” is a claim to test, not to trust. I have flagged exactly this on Azure’s Schema Registry, where Confluent-API support reportedly exists and still needed hands-on testing to confirm whether standard serdes work or a custom one is required. An hour in a sandbox with your actual client libraries beats an architecture built on a compatibility matrix’s checkmark.