← Back to list

local-first-sync
by BenderFendor
⭐ 0🍴 0📅 Jan 23, 2026
SKILL.md
name: local-first-sync description: Implement a local-first + backend-sync data flow (create/update/delete) with tombstones, dedupe, and safe retry. Use when building offline-capable entities (highlights, annotations, queue items, notes) that must persist immediately in localStorage while syncing to a server.
Local-First Sync
Overview
This skill provides a reusable, minimal pattern for local-first persistence with background server sync, including dedupe/merge rules, tombstones for deletes, and a safe retry model that avoids infinite loops.
Use it when the user requires:
- Instant local persistence (works offline)
- Backend as eventual consistency store
- Create/update/delete support
- Explicit handling for failures and retries
Assumptions (state explicitly)
- Whether backwards compatibility is required.
- Whether the backend supports idempotency keys or client-provided ids.
- What determines identity (server id, natural key, or fingerprint).
Data Model
Store local records with:
client_id(UUID)server_id?sync_status:synced | pending | failedpending_op:create | update | deletelast_error?(string)local_updated_at(ISO)deleted?(tombstone)
Recommendation: keep local records as a superset of server records (so rendering code can stay simple).
Storage Keys
- Prefer versioned keys:
entity:v1:<entity_scope_or_id> - Scope keys by natural grouping (e.g.
highlights:v1:${article_url}) to avoid large global blobs.
Core Workflow
1) Read path (open modal / load view)
- Load local state immediately and render.
- Fetch server state.
- Merge + dedupe server into local.
- Persist merged local state.
- Kick off background sync for
pending/failedops.
2) Write path (create/update/delete)
Always do the local write first:
- Create/update/delete in local store; mark
pending_opandsync_status=pending(or keep tombstone for delete). - Render from local store.
- Trigger background sync.
3) Sync loop (background)
For each record with pending_op:
create: POST to server, then store returnedserver_idand clear pending fields.update: PATCH to server (requiresserver_id), clear pending fields.delete: DELETE on server ifserver_idexists; then remove local record.
Failure behavior:
- Mark local record
sync_status=failedand setlast_error. - Do not tight-loop retries; retry on explicit user action (button) and optionally on reconnect.
Merge + Dedupe Strategy
Identity approach (strong → weak):
server_id- Fingerprint of stable fields (example):
${start}:${end}:${normalized(text)}
Merge rules (common default):
- Keep local tombstones: if local says
deletedandpending_op=delete, do not resurrect from server. - Prefer newer note/edit: compare
local_updated_atvs serverupdated_atif available. - Preserve local
client_idandsync_status.
UI Guidance
- Show sync state near the entity list:
Synced | Saving | Offline | Failed. - Add a
Retry syncbutton whenfailed. - Don’t block user writes when offline; queue them.
Testing Checklist
- Dedupe: server merge doesn’t create duplicates.
- Tombstones: pending delete doesn’t resurrect.
- Offline: create/update/delete marks
pendingand persists locally. - Failure: marks
failedand recordslast_error. - Retry: transitions
failed → pending → synced.
Debug Logging Guidance
- Log one-line summary per sync run (counts: pending/failed/synced).
- Log failing entity
client_id,server_id,pending_op, and request URL. - Avoid logging full article text or other large/PII payloads.
Score
Total Score
50/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon