TL;DR
- Sync must be idempotent. Re-running a gateway sync should converge, not explode on the second pass — adopt what already exists instead of erroring on a UNIQUE conflict.
- Paginate for real. A list API that caps at 100 will lie to you the moment you have 101 records. Fetch every page before you diff.
- Outgoing mail can be observable without a third-party service — Laravel's mail events give you delivered/opened/clicked hooks.
- Elsewhere: ITIL ticket types + major-incident handling on a support desk, a Flutter mobile client wired to a REST v1 API, and a breadcrumb bug that was really an ownership bug.
The gateway thread: make sync boring
Most of today ran through an API-gateway management app. The sync fixes got their own write-up (the idempotent-sync post in this series).
Short version: syncing config into a gateway is a converge operation, not an insert. If a target already exists, adopt it on the 409 instead of throwing. If a list endpoint pages, read all the pages — a partial read makes your diff hallucinate deletions. And a scoped plugin must never silently widen to global scope; scope is identity, not a detail you can drop.
Mail you can actually trace
An identity portal picked up delivered/opened/clicked tracking on outgoing mail — no external ESP, just Laravel's own MessageSending / MessageSent events plus a tracking pixel and link rewriting (full write-up of its own). The nice part: it's a runtime toggle, so you can turn tracking off per-environment without a deploy.
Support desk: incidents are a different shape than tickets
A support platform got ITIL ticket types and major-incident management. The lesson underneath: a major incident isn't just a high-priority ticket. It has its own lifecycle — declare, coordinate, stand down — and its own audience. Modelling it as a first-class thing (declare/stand-down actions, internal work items) beats overloading the priority field and hoping.
Same platform also got emoji reactions, reply editing, soft-delete with trash recovery, a notifications inbox, and analytics wired end-to-end. A companion Flutter mobile client came online too, talking to a fresh REST v1 API for auth, tickets, and attachments.
| Change | Why it matters |
|---|---|
| Soft-delete + trash restore | Deletion should be reversible; a confirm step guards the irreversible part |
| Notifications inbox + unread badge | Pull the user back without email noise |
| REST v1 for the mobile app | One versioned contract for web and mobile, not two divergent APIs |
The small one that taught the most
An importer package had hard-coded breadcrumbs in its views. The fix was to delete them — the host app already renders breadcrumbs globally. Rendering them twice isn't a styling bug, it's a boundary violation: the package overstepped into layout that isn't its job.
Takeaway
Three ideas carried the day: sync converges, so make it idempotent; observability can be built from framework primitives before you reach for a service; and know where a package's job ends — don't render what the host already owns.
Top comments (0)