Canberk Şenyüz

Tidewater

Client
Freight forwarder
Year
2025
Role
Backend engineer
Stack
TypeScript, Node.js, PostgreSQL, Redis
The project summary as it is stored: 91 bytes of UTF-8.

A tracking service that turns every carrier’s feed into one honest timeline per shipment.

Context

Carriers send status updates in different formats and at different times, sometimes twice and sometimes out of order. The customer portal stored a single status field and overwrote it on every message, so customers saw shipments jump backwards and called support to ask which status was true.

Idea

Stop overwriting. Every carrier message becomes an immutable event, and the timeline is derived from the events. Order comes from the time the carrier says something happened, not the time we heard about it, and a late message simply causes that one timeline to be rebuilt.

RowReceivedOccurredCarrierEvent
000009:0208:55Carrier APicked up
001009:4009:31Carrier ADeparted hub
002010:1507:58Carrier BBooking confirmed
003011:0310:47Carrier AArrived at hub
004011:0910:47Carrier AArrived at hub
Events in the order they arrived. The selected one is late: the timeline is rebuilt with it at 07:58. The last line repeats an earlier one, hashes to an existing key and is dropped.

Details

  1. Idempotent ingest

    Each event gets a key hashed from carrier, reference, code and event time. Duplicates hit a unique index and disappear, so retries are safe everywhere.

  2. A small shared vocabulary

    Carrier codes map to eleven internal states through one table that support staff can read. New carriers are a data change, not a deploy.

  3. Rebuild, never patch

    When an event arrives late, the service re-derives that shipment’s timeline from scratch. It is cheaper than it sounds and it removed a whole class of ordering bugs.

const key = sha256([carrier, ref, code, occurredAt.toISOString()].join('|')); await db.query(  `insert into events (key, shipment_id, code, occurred_at, raw)   values ($1, $2, $3, $4, $5)   on conflict (key) do nothing`,  [key, shipmentId, normalize(carrier, code), occurredAt, raw],); await rebuildTimeline(shipmentId);
TypeScript The ingest path: hash, insert or ignore, rebuild.

Outcome

Shipped as the single source for the customer portal and for the support team’s internal view, so both now show the same history in the same order.