apache/kafka
Patterns and conventions
Conventions a reader will trip over when first reading the Kafka codebase. Most are enforced by Checkstyle, SpotBugs, or Spotless; some are folklore that PR review will surface.
Two languages, one project
- Java is the primary language and is required for any new public API.
- Scala is concentrated in
core/(the broker request layer, replication, log management) and a thinstreams/streams-scala/wrapper. New Scala code is generally only added incore/; new modules are written in Java. - The build sets
releaseto Java 11 forclientsandstreamsso that they remain consumable on Java 11 runtimes; everything else compiles for Java 17. - Scala 2.13 is the only supported Scala version.
Public API rules
- Anything outside an
internals/package is part of the public surface. Changing or removing it requires a KIP. - All public types must have Javadoc. Public methods must document parameters, return values, and exceptions.
- Public classes default to
finalunless they're explicitly designed to be subclassed. @InterfaceStabilityannotations (Stable,Evolving,Unstable) live inclients/src/main/java/org/apache/kafka/common/annotation/InterfaceStability.javaand are the source of truth for binary-compatibility expectations.
internals packages
Almost every public package has a sibling internals/ package containing implementation classes. Anything in internals/ may be changed without a KIP, and clients must not import from it. Examples:
clients/.../producer/internals/Sender.java— the producer's network thread; the publicKafkaProducerorchestrates it.clients/.../consumer/internals/Fetcher.java,ConsumerNetworkThread.java,MembershipManager.java.streams/.../streams/internals/,streams/.../streams/processor/internals/.
When you find yourself reaching into internals from outside the module, that's usually a sign that the public surface needs to grow (a KIP) or that the call site belongs in the same module.
RPC schemas, not handcrafted serialization
Every request/response is described by a JSON schema in clients/src/main/resources/common/message/. The generator/ module compiles these into Java classes during processMessages. Treat this as the single source of truth:
- Adding a new RPC = new
*.jsonschema + KIP. Don't write the request/response by hand. - Adding a field to an existing RPC = bump the message version in the schema; schema must declare which versions support the field.
- The corresponding
ApiKeysenum value lives inclients/src/main/java/org/apache/kafka/common/protocol/ApiKeys.java;ApiMessageType.jsonis the master list.
Configuration
- Each subsystem owns a
*Configclass (KafkaConfig,ProducerConfig,ConsumerConfig,StreamsConfig,ConnectConfig,KRaftConfigs, etc.) that declares all options viaConfigDef(clients/src/main/java/org/apache/kafka/common/config/ConfigDef.java). - Each option must have a name, type, default, importance, and description string. Descriptions become user-facing docs.
- Adding/renaming/removing a configuration requires a KIP and a release-notes entry.
- For broker configs, dynamic (cluster-level / broker-level) overrides go through
DynamicBrokerConfig(core/src/main/scala/kafka/server/DynamicBrokerConfig.scala).
Error handling
- Public exceptions live in
clients/src/main/java/org/apache/kafka/common/errors/and are typed by theErrorsenum (clients/.../common/protocol/Errors.java), which maps numeric error codes to exception classes. Adding anErrorsvalue requires a KIP. - Retryable vs non-retryable is part of the type —
RetriableExceptionsubclasses are retried automatically by clients. KafkaExceptionis the unchecked supertype; use it sparingly, and prefer the most specific subclass.- Don't swallow
InterruptedException— re-set the interrupt flag and propagate.
Threading and concurrency
- The producer is built around two threads: the application thread that calls
send()and theSenderthread that drives I/O. They communicate via a lock-freeRecordAccumulatorand aDequeper partition. - The consumer's new (KIP-848) implementation uses a single
ConsumerNetworkThreadplus the application thread; the legacy implementation has a heartbeat thread additionally. - The broker's
KafkaApisis invoked on the request handler pool (KafkaRequestHandler); long-running work (e.g., delayed fetches, fetch sessions) is offloaded to purgatories and a backgroundkafka-schedulerpool. - Use the project's
Timeinterface (clients/.../common/utils/Time.java) instead ofSystem.currentTimeMillis()— it's mockable in tests viaMockTime. - Use the project's
KafkaThreadfactory (clients/.../common/utils/KafkaThread.java) instead of rawnew Thread(); it sets daemon and uncaught-exception-handler defaults.
Coding style
- Two-space indentation in Scala; four-space in Java (Checkstyle enforces).
- 120-column line length.
- Imports must be ordered exactly as Spotless wants —
./gradlew spotlessApplywill fix them. - Don't use
varfor class-level state; preferfinaland explicit setters. - Don't introduce wildcard imports.
- Don't suppress Checkstyle/SpotBugs warnings without an inline comment justifying it.
Testing conventions
See Testing. The shorthand:
- JUnit 5; Mockito for mocks; no PowerMock.
MockTime, neverThread.sleep, in unit tests.@Tag("integration")for tests that boot a broker; everything else is unit.TestUtils.waitForConditionfor asynchronous assertions; never busy-spin.
Logging
- Use SLF4J (
org.slf4j.Logger); do not import Log4j directly. - Pre-compute message strings only when the level is enabled or use the
{}placeholder syntax — Kafka has been bitten byString.formatin tight loops. - For frequent debug output, gate with
if (log.isDebugEnabled())if formatting is non-trivial. - Don't log at
INFOfrom per-record code paths; reserveINFOfor lifecycle events (startup, shutdown, partition state changes, rebalance results).
Backward compatibility
- All public APIs must remain source- and binary-compatible across minor releases.
- Wire protocol versioning is part of every RPC schema; never remove a version still supported by a current client.
- On-disk formats are versioned via
RecordVersion(clients) andMetadataVersion(controller); upgrades go through explicit version transitions, never silent format changes.
Pull request hygiene
- One JIRA per PR.
- Title starts with the JIRA key.
- Squash commits in your branch before review (the merger will squash again at merge time, but reviewers benefit from a clean history).
- Don't merge unrelated refactoring into a feature PR; open a separate
MINOR:PR for it. - Update
docs/for any user-visible change.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.