Back to Kafka Fundamentals: Topics, Partitions, Offsets
Q1Question 1 of 18

When would you deliberately choose NOT to use Kafka?

Key Concepts
  • Skip Kafka for synchronous request-reply where a direct REST or gRPC call is simpler and faster.
  • Low-volume workloads are better served by a managed queue like SQS with near-zero operational cost.
  • Per-message priority and complex routing are native in RabbitMQ or ActiveMQ but awkward in Kafka.
  • Small teams without operational maturity should use a fully managed Kafka service or a simpler queue.
  • Transactional multi-table operations with foreign keys and joins belong in a database, not a log.
  • Kafka shines for high-throughput ingestion, event sourcing, replayable history, and fan-out to many consumers.
  • The decision is about matching the tool to the access pattern, not defaulting to Kafka for everything.

Definition

Skip Kafka for simple request-reply, for low-volume or tiny-team workloads where a managed queue is cheaper, for priority or complex routing that a classic broker does natively, and when a database transaction is actually the right tool.
  • Request-reply -- if a caller needs an immediate answer, such as a fraud check, a direct REST or gRPC call is simpler than publishing a request and correlating a reply through Kafka.
  • Low volume -- for a few hundred messages a day, Kafka's operational cost dwarfs the benefit; a managed queue like SQS gives the same decoupling with almost no operations.
  • Priority and routing -- per-message priority, complex header-based routing, and per-message TTL are native in RabbitMQ or ActiveMQ but require awkward workarounds in Kafka.
  • Small teams -- running Kafka well requires understanding partitions, replication, consumer groups, and rebalancing; a small team is better served by a managed queue or a fully managed Kafka service.
  • Transactional joins -- if the real need is atomically updating several tables with ad-hoc queries and foreign keys, that is a database job, not a log.

Kafka is like building a dedicated high-speed conveyor system through National Bank of India that keeps a permanent record of every item and lets many departments read it at their own pace. That is fantastic for a flood of transactions feeding many systems. But if a teller just needs a yes-or-no answer from the credit desk right now, you do not build a conveyor, you make a phone call. And if the mailroom only handles a few letters a day, buying and maintaining an industrial conveyor is wasteful.

Understanding the Concepts

When would you choose NOT to use Kafka?

Kafka is a distributed, partitioned, replicated commit log optimized for high-throughput streaming, replayable event history, and many consumers. You deliberately avoid it when its strengths do not match the problem.

Why not use Kafka for synchronous request-reply?

If a caller needs an immediate answer, such as a fraud check that must return approve or decline before a transaction proceeds, a direct REST or gRPC call is simpler and clearer. You can do request-reply over Kafka, but it adds correlation ids, reply topics, and latency for no benefit when a simple call suffices.

JAVA
package com.bank.service;

import java.math.BigDecimal;

// CORRECT: synchronous fraud check via direct call
public class FraudCheckService {
    public boolean checkTransaction(String accountId, BigDecimal amount) {
        // Direct REST call -- immediate response, no Kafka overhead
        System.out.println("Checking fraud for " + accountId + " amount: " + amount);
        boolean approved = amount.compareTo(new BigDecimal("1000000")) < 0;
        System.out.println("Result: " + (approved ? "APPROVED" : "DECLINED"));
        return approved;
    }

    public static void main(String[] args) {
        FraudCheckService service = new FraudCheckService();
        // Rajesh's card swipe needs instant decision -- REST, not Kafka
        service.checkTransaction("SAV2001", new BigDecimal("50000"));
    }
}
TEXT
Expected output:
Checking fraud for SAV2001 amount: 50000
Result: APPROVED

Why not use Kafka for low-volume workloads?

Kafka is a stateful distributed system with brokers, replication, and the KRaft controller quorum to operate, patch, and monitor. If you move a few hundred or a few thousand messages a day, the operational cost dwarfs the benefit, and a managed queue such as Amazon SQS gives the same decoupling with almost no operations.

| Aspect | Kafka | Managed Queue (SQS) |
|---|---|---|
| Messages per day | Millions+ | Hundreds to thousands |
| Operations burden | High (brokers, replication, monitoring) | Near zero |
| Cost at low volume | High (cluster always running) | Pay per message |
| Use case fit | High-throughput streaming | Simple decoupling |

JAVA
package com.bank.service;

// WRONG: using Kafka for 10 end-of-day notification emails
// A Kafka cluster running 24/7 for 10 messages/day is over-engineering

// CORRECT: use a managed queue
public class EndOfDayNotifier {
    public void sendNotification(String accountId, String message) {
        // Send to SQS or a simple job table -- no Kafka cluster needed
        System.out.println("Queued notification for " + accountId + ": " + message);
    }

    public static void main(String[] args) {
        EndOfDayNotifier notifier = new EndOfDayNotifier();
        notifier.sendNotification("SAV2001", "Daily statement ready for Rajesh");
    }
}
TEXT
Expected output:
Queued notification for SAV2001: Daily statement ready for Rajesh

Why not use Kafka for priority queues and complex routing?

Kafka has no native priority queue and no native per-message TTL. If you need per-message priority, where urgent VIP wire transfers jump ahead of ordinary transfers, or complex routing by headers and patterns, RabbitMQ or ActiveMQ handle these out of the box. Simulating them with multiple topics and custom logic in Kafka is awkward.

JAVA
package com.bank.service;

// WRONG: simulating priority in Kafka with multiple topics
// transfers-high, transfers-medium, transfers-low
// Consumer must poll all three and prioritize manually -- awkward

// CORRECT: use RabbitMQ with native priority queue
public class PriorityTransferExample {
    public static void main(String[] args) {
        // RabbitMQ queue declared with x-max-priority: 10
        // Each message carries a priority header
        System.out.println("VIP wire for Priya: priority 10 -- processed first");
        System.out.println("Regular transfer for Rajesh: priority 5 -- processed after VIP");
        // RabbitMQ delivers highest priority first natively
    }
}
TEXT
Expected output:
VIP wire for Priya: priority 10 -- processed first
Regular transfer for Rajesh: priority 5 -- processed after VIP

Why not use Kafka for small teams?

Running Kafka well means understanding partitions, replication, in-sync replicas, consumer groups, rebalancing, retention, and monitoring lag. A small team without that capacity is better served by a fully managed queue or even a database-backed job table. If you do need Kafka's model, a fully managed Kafka service removes most of the operational burden.

JAVA
package com.bank.service;

// For a small team: a database-backed job table is simpler than Kafka
public class DatabaseJobQueue {
    public void enqueue(String jobType, String payload) {
        // INSERT INTO jobs (type, payload, status) VALUES (?, ?, 'PENDING')
        System.out.println("Enqueued job: " + jobType + " -> " + payload);
    }

    public void processNext() {
        // SELECT ... WHERE status='PENDING' ORDER BY created_at LIMIT 1 FOR UPDATE
        System.out.println("Processing next pending job from database");
    }

    public static void main(String[] args) {
        DatabaseJobQueue queue = new DatabaseJobQueue();
        queue.enqueue("TRANSFER", "SAV2001 -> SAV3001 amount 25000");
        queue.processNext();
    }
}
TEXT
Expected output:
Enqueued job: TRANSFER -> SAV2001 -> SAV3001 amount 25000
Processing next pending job from database

Why not use Kafka for transactional database work?

If you need to atomically update several business tables, run ad-hoc queries, enforce foreign keys, or join across entities, that is a database job. Kafka is a log, not a query engine, so forcing relational, transactional work through it is the wrong tool.

JAVA
package com.bank.service;

import java.math.BigDecimal;

// CORRECT: use database transactions for multi-table atomic operations
public class TransferService {
    // @Transactional -- one database transaction for all three operations
    public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
        // debit(fromAccount, amount);   -- UPDATE accounts SET balance = balance - ?
        // credit(toAccount, amount);    -- UPDATE accounts SET balance = balance + ?
        // insertAuditLog(fromAccount, toAccount, amount); -- INSERT INTO audit_log
        System.out.println("Atomic transfer: " + fromAccount + " -> "
            + toAccount + " amount: " + amount);
        // All three succeed or all three roll back -- database ACID, not Kafka
    }

    public static void main(String[] args) {
        TransferService service = new TransferService();
        service.transfer("SAV2001", "SAV3001", new BigDecimal("50000"));
    }
}
TEXT
Expected output:
Atomic transfer: SAV2001 -> SAV3001 amount: 50000

When SHOULD you use Kafka?

Kafka shines for high-throughput ingestion, event sourcing, replayable history, stream processing, and fan-out to many independent consumers.

| Use Case | Right Tool | Why |
|---|---|---|
| Fraud check (instant response) | REST / gRPC | Synchronous request-reply |
| 10 emails/day | SQS / managed queue | Low volume |
| VIP wire priority | RabbitMQ | Native priority queue |
| Multi-table ACID | Database transaction | Relational joins, foreign keys |
| 50K transactions/second firehose | Kafka | High throughput, replay, fan-out |
| Event sourcing with replay | Kafka | Immutable log, replayable history |
| Analytics + fraud + ledger from same stream | Kafka | Multiple independent consumers |

What should a senior engineer know?

These are not absolutes. Kafka adds an asynchronous hop and at-least-once semantics that force you to design for idempotency and out-of-order delivery, which is unnecessary complexity when the interaction is naturally synchronous and exactly one caller cares about the result. The decision is about matching the tool to the access pattern. A fully managed Kafka service changes the operational calculus for small teams.

Visual Overview

DIAGRAM
 Decision Tree: Should You Use Kafka?
 =====================================

                  ┌──────────────────────┐
                  │ Need immediate reply? │
                  └──────────┬───────────┘
                     yes     │     no
                  ┌──────┐   │   ┌──────────────────┐
                  │ REST │   │   │ High volume?      │
                  │ gRPC │   │   │ (1000+ msg/sec)   │
                  └──────┘   │   └────────┬──────────┘
                             │    yes     │     no
                             │   ┌────────┘   ┌──────────────┐
                             │   │            │ Managed Queue │
                             │   │            │ (SQS, etc.)  │
                             │   │            └──────────────┘
                             │   ▼
                  ┌──────────────────────┐
                  │ Need priority queue? │
                  │ Complex routing?     │
                  └──────────┬───────────┘
                     yes     │     no
                  ┌──────────┐   ┌──────────────┐
                  │ RabbitMQ │   │   Kafka       │
                  │ ActiveMQ │   │   (correct    │
                  └──────────┘   │    choice)    │
                                 └──────────────┘

Key Takeaway

Kafka is a distributed commit log optimized for high throughput, replayable history, stream processing, and fanning out to many independent consumers. I choose not to use it when the problem does not match those strengths. The clearest case is synchronous request-reply: if Rajesh's card swipe needs an instant fraud decision, a direct REST call is simpler and faster than publishing a request and correlating a reply through Kafka.

The second case is low volume, where Kafka's operational cost dwarfs the benefit. The third is rich messaging features like per-message priority that traditional brokers provide natively. The fourth is small teams without operational maturity, and the fifth is transactional relational work that belongs in a database.

In my National Bank of India work, I made exactly these calls. The card authorization path was synchronous REST. The handful of end-of-day emails went on a managed queue. A priority-sensitive wire path used a classic broker. And the transaction firehose that fed analytics, fraud, and the ledger with replay was the workload that genuinely belonged on Kafka.

Remember one sentence: Kafka is for high-throughput, replayable, multi-consumer streaming -- use a direct call for request-reply, a managed queue for low volume, and a traditional broker for priority routing.

---

Share
Use ← → arrow keys to navigate