Pick the protocol for the constraint, not the hype
For anything running on constrained hardware over unreliable links, MQTT is still the default recommendation: a tiny binary header, a publish/subscribe model that decouples devices from consumers, and built-in quality-of-service levels. Reach for CoAP if you're on very low-power devices doing request/response over UDP, and reserve plain HTTP for devices that are already comfortably powered and networked.
QoS 0 for high-frequency telemetry you can afford to drop; QoS 1 for events that must arrive but tolerate duplicates; QoS 2 only when a duplicate reading would actually cause harm. It's the most expensive option for a reason.
The gateway pattern for unreliable connectivity
Devices in the field lose connectivity. An edge gateway that buffers locally and forwards on reconnect turns "the network dropped for ten minutes" from a data-loss incident into a non-event.
// Simplified edge buffering loop
while (device.hasReading()) {
reading = device.nextReading();
localBuffer.append(reading);
if (broker.isConnected()) {
flushBufferedReadings(localBuffer, broker);
}
}
Bridging device events into the backend
This is where an IoT deployment and a backend event-driven architecture meet. An MQTT broker with a Kafka bridge (or a managed equivalent) lets device telemetry land in the same event backbone your services already consume, so a temperature reading and an order-created event can flow through the same durable log, replay tooling, and consumer patterns.
Security is provisioning, not an afterthought
- Unique credentials per device, never one shared key baked into a firmware image. A single leaked shared secret compromises the entire fleet.
- Mutual TLS for device-to-broker connections wherever the hardware can support it.
- Signed OTA updates with a rollback path. An update that bricks a device you can't physically reach is a much worse day than a delayed feature.
Recommendations, concretely
If I were starting today: Eclipse Mosquitto or EMQX as the broker (EMQX if you need horizontal scale and built-in bridging), MQTT over TLS with per-device certificates, and a small edge gateway process for buffering. Even something as simple as SQLite as a local queue is enough to survive most connectivity gaps.
Design for the device being offline, not for the device being online. Everything else (retries, buffering, idempotent consumers) falls out of that one assumption.