Device Communication System with MQTT

2026-02-04

1. MQTT Is a Communication Model, Not a Feature

A common mistake is to think of MQTT as a “lighter HTTP.”

That assumption breaks quickly.

HTTP assumes:

  • Stable connectivity
  • Synchronous request–response
  • Short-lived interactions

MQTT assumes the opposite:

  • Disconnections are normal
  • Messages are asynchronous events
  • Systems are long-running

In MQTT:

  • Topics are streams of intent, not endpoints
  • Brokers route, they don’t interpret
  • Messages describe state changes, not function calls

Once I stopped forcing HTTP mental models onto MQTT, many design problems disappeared.


2. What Mosquitto Does — and Intentionally Does Not Do

Mosquitto is minimal by design. It doesn’t try to solve your business problems, and it doesn’t hide protocol behavior behind abstractions. Every decision — persistence, retain, QoS, sessions — is explicit.

Mosquitto’s responsibility is clear and narrow:

  • Accept connections
  • Authenticate clients
  • Route messages
  • Apply QoS and persistence rules

What it intentionally does not do:

  • Manage device state
  • Execute workflows
  • Understand business semantics

Service should not depend on the broker “understanding” commands, that coupling will surface later as rigidity or hidden bugs.


3. The Real Problems in Device Communication

Before thinking about topics or configuration, I found it useful to enumerate the real problems:

Device identity

  • Who is this device?
  • What is it allowed to publish or subscribe to?

Online vs offline

  • A TCP connection does not mean availability
  • Offline is a valid, expected state

Delivery vs execution

  • Was the command received?
  • Was it executed?
  • Did the device reboot in between?

Uncertainty

  • Messages may be duplicated
  • Messages may arrive late or out of order
  • Devices reconnect and replay state

MQTT does not remove these problems — it exposes them.


4. Topic Design as System Design

Early on, I tried to “simplify” by using fewer topics.
That turned out to be a mistake.

Now I treat topic design as part of system modeling:

  • Command topic
    Service → Device
    Represents intent

  • Acknowledgement topic
    Device → Service
    Confirms receipt

  • Result topic
    Device → Service
    Confirms execution

  • Status / heartbeat topic
    Device → Service
    Describes current state

Each topic answers a different question.
Combining them saves lines of configuration but costs clarity.


5. QoS, Retain, and Persistence: Hard Choices

MQTT gives you powerful tools, but they are not free.

Retain

  • Never retain commands
    • A retained command can execute hours or days later
  • Retain is useful for state, not intent

QoS

  • QoS 0: fast, unreliable
  • QoS 1: “at least once” — often the sweet spot
  • QoS 2: rarely worth the complexity

Persistence

  • Persistence protects against broker restarts
  • It does not protect against bad semantics

Reliability is not achieved by turning on every option — it comes from aligning configuration with meaning.


6. Treating MQTT as a Concurrent System on the Backend

From the backend’s point of view, MQTT is a concurrent event stream.

That means:

  • Messages must be idempotent
  • State transitions must be explicit
  • Timeouts must be intentional

I found it helpful to model command handling as a state machine:

  • Created
  • Sent
  • Acknowledged
  • Executed
  • Timed out or failed

This framing aligns well with how distributed systems behave under partial failure.


7. Clear Boundaries: Broker vs Backend

Using Mosquitto made responsibility boundaries obvious:

  • Broker (Mosquitto)
    • Transport
    • Routing
    • Delivery semantics
  • Backend services
    • Authorization
    • State storage
    • Command lifecycle
    • Business logic

By keeping Mosquitto “dumb,” the backend becomes easier to test, reason about, and evolve.


8. Mistakes I Made While Learning MQTT

Some mistakes only become visible in production-like conditions:

  • Treating MQTT like RPC
  • Trusting online status too much
  • Overusing retain
  • Assuming messages arrive exactly once
  • Ignoring reconnect semantics

Most MQTT issues are not obvious bugs — they are design mismatches that surface slowly.