20 ago 2025·8 min di lettura

Risoluzione dei conflitti per moduli offline-first con Kotlin + SQLite

Scopri la risoluzione dei conflitti in moduli offline-first: regole di merge chiare, un semplice flusso di sync Kotlin + SQLite e pattern UX pratici per conflitti di editing.

Risoluzione dei conflitti per moduli offline-first con Kotlin + SQLite

Cosa succede davvero quando due persone modificano offline

I form offline-first permettono alle persone di visualizzare e modificare dati anche quando la rete è lenta o assente. Invece di aspettare il server, l’app scrive prima le modifiche in un database SQLite locale, quindi sincronizza più tardi.

Questo dà la sensazione di immediatezza, ma crea una realtà semplice: due dispositivi possono cambiare lo stesso record senza conoscersi.

Un conflitto tipico è questo: un tecnico apre un ordine di lavoro su un tablet in un seminterrato senza segnale. Marca lo stato come "Done" e aggiunge una nota. Allo stesso tempo, un supervisore su un altro telefono aggiorna lo stesso ordine, lo riassegna e cambia la data di scadenza. Entrambi premono Salva. Entrambi i salvataggi riescono localmente. Nessuno ha fatto niente di sbagliato.

Quando finalmente avviene la sincronizzazione, il server deve decidere quale sia il record “reale”. Se non gestisci i conflitti esplicitamente, di solito finisci con una di queste situazioni:

  • Last write wins: la sincronizzazione più recente sovrascrive le modifiche precedenti e qualcuno perde dati.
  • Hard failure: la sincronizzazione rifiuta un aggiornamento e l’app mostra un errore poco utile.
  • Record duplicati: il sistema crea una copia per evitare di sovrascrivere, e i report diventano confusi.
  • Merge silenzioso: il sistema combina le modifiche ma mescola i campi in modi inattesi agli utenti.

I conflitti non sono un bug. Sono il risultato prevedibile del permettere alle persone di lavorare senza una connessione live, che è proprio lo scopo dell’approccio offline-first.

L’obiettivo è duplice: proteggere i dati e mantenere l’app facile da usare. Questo di solito significa regole di merge chiare (spesso a livello di campo) e un’esperienza utente che interrompe le persone solo quando è veramente necessario. Se due modifiche toccano campi diversi, spesso puoi fondere tutto in modo silenzioso. Se due persone cambiano lo stesso campo in modi diversi, l’app dovrebbe rendere la cosa visibile e aiutare qualcuno a scegliere il risultato corretto.

Scegli una strategia di conflitto che corrisponda ai tuoi dati

I conflitti non sono prima di tutto un problema tecnico. Sono una decisione di prodotto su cosa significhi “corretto” quando due persone hanno modificato lo stesso record prima della sincronizzazione.

Tre strategie coprono la maggior parte delle app offline:

  • Last write wins (LWW): accetta la modifica più recente e sovrascrivi la precedente.
  • Revisione manuale: fermati e chiedi a una persona quale versione mantenere.
  • Merge a livello di campo: combina le modifiche per campo e chiedi solo quando due persone hanno toccato lo stesso campo.

LWW può andare bene quando la velocità è più importante della precisione assoluta e il costo di sbagliare è basso. Pensaci per note interne, tag non critici o uno stato di bozza che si può modificare di nuovo.

La revisione manuale è la scelta più sicura per campi ad alto impatto dove l’app non dovrebbe indovinare: testi legali, conferme di conformità, importi di pagamenti e fatture, dettagli bancari, istruzioni su farmaci e tutto ciò che può creare responsabilità.

Il merge a livello di campo è generalmente la scelta migliore per i form dove ruoli diversi aggiornano parti diverse. Un addetto al supporto modifica l’indirizzo mentre il commerciale aggiorna la data di rinnovo. Un merge per campo mantiene entrambe le modifiche senza disturbare nessuno. Ma se entrambi hanno modificato la data di rinnovo, quel campo dovrebbe innescare una decisione.

Prima di implementare qualsiasi cosa, scrivi cosa significa “corretto” per il tuo business. Una checklist rapida aiuta:

  • Quali campi devono sempre riflettere il valore reale più recente (per es. lo stato attuale)?
  • Quali campi sono storici e non dovrebbero mai essere sovrascritti (per es. un timestamp di invio)?
  • Chi può cambiare ogni campo (ruolo, proprietà, approvazioni)?
  • Qual è la fonte di verità quando i valori divergono (dispositivo, server, approvazione manager)?
  • Cosa succede se scegli male (fastidio minore vs impatto finanziario o legale)?

Quando queste regole sono chiare, il codice di sync ha un solo compito: farle rispettare.

Definisci regole di merge per campo, non per schermata

Quando si verifica un conflitto, raramente colpisce uniformemente tutto il form. Un utente può aggiornare un numero di telefono mentre un altro aggiunge una nota. Se tratti l’intero record come tutto-o-nulla, costringi le persone a rifare lavoro già valido.

Il merge a livello di campo è più prevedibile perché ogni campo ha un comportamento noto. L’UX resta calma e veloce.

Un modo semplice per iniziare è separare i campi in categorie “di solito sicuri” e “di solito non sicuri”.

Di solito sicuri da unire automaticamente: note e commenti interni, tag, allegati (spesso unione), e timestamp come last contacted (spesso tenere il più recente).

Di solito non sicuri da unire automaticamente: status/stato, assignee/proprietario, totali/prezzi, flag di approvazione e conteggi di inventario.

Poi scegli una regola di priorità per ogni campo. Scelte comuni sono server wins, client wins, role wins (per esempio, il manager sovrascrive l’agente) o un tie-breaker deterministico come la versione server più recente.

La domanda chiave è cosa succede quando entrambe le parti hanno modificato lo stesso campo. Per ogni campo, scegli un comportamento:

  • Auto-merge con una regola chiara (per esempio, i tag sono una union)
  • Conservare entrambi i valori (per esempio, appendi le note con autore e orario)
  • Segnalare per revisione (per esempio, status e assignee richiedono una scelta)

Esempio: due operatori del supporto modificano lo stesso ticket offline. Operatore A cambia status da "Open" a "Pending". Operatore B modifica le notes e aggiunge il tag "refund". Alla sincronizzazione puoi fondere notes e tags in sicurezza, ma non dovresti fondere silenziosamente status. Mostra solo status per la scelta, mentre il resto è già unito.

Per evitare dibattiti dopo, documenta ogni regola in una frase per campo:

  • "notes: tieni entrambe, appendi il più recente per ultimo, includi autore e orario."
  • "tags: union, rimuovi solo se è stato rimosso esplicitamente su entrambe le parti."
  • "status: se modificato da entrambe le parti, richiede scelta utente."
  • "assignee: manager wins, altrimenti server wins."

Quella frase diventa la fonte di verità per il codice Kotlin, le query SQLite e l’interfaccia di risoluzione conflitti.

Nozioni di base sul modello dati: versioni e campi di audit in SQLite

Se vuoi che i conflitti siano prevedibili, aggiungi un piccolo set di colonne di metadata a ogni tabella sincronizzata. Senza di essi, non puoi sapere se stai guardando una modifica recente, una copia vecchia o due modifiche che richiedono un merge.

Un minimo pratico per ogni record sincronizzato dal server:

  • id (primary key stabile): non riutilizzarla mai
  • version (intero): incrementa ad ogni scrittura accettata dal server
  • updated_at (timestamp): quando il record è stato modificato l’ultima volta
  • updated_by (text o user id): chi ha fatto l’ultima modifica

Sul dispositivo, aggiungi campi locali per tracciare le modifiche non confermate dal server:

  • dirty (0/1): esistono modifiche locali
  • pending_sync (0/1): in coda per l’upload, ma non confermato
  • last_synced_at (timestamp): ultima volta in cui questa riga corrispondeva al server
  • sync_error (text, opzionale): ultima ragione di errore da mostrare in UI

La concorrenza ottimistica è la regola più semplice per evitare sovrascritture silenziose: ogni update include la versione che si pensa di modificare (un expected_version). Se il record server è ancora a quella versione, l’aggiornamento viene accettato e il server restituisce la nuova versione. Altrimenti c’è un conflitto.

Esempio: Utente A e Utente B hanno scaricato version = 7. A sincronizza prima; il server sale a 8. Quando B prova a sincronizzare con expected_version = 7, il server rifiuta con un conflitto così l’app di B fa il merge invece di sovrascrivere.

Per una buona schermata di conflitto, conserva il punto di partenza condiviso: da quale stato l’utente ha iniziato a modificare. Due approcci comuni:

  • Conservare uno snapshot dell’ultimo record sincronizzato (una colonna JSON o una tabella parallela).
  • Conservare un change log (una riga per edit o una riga per campo modificato).

Gli snapshot sono più semplici e spesso sufficienti per i form. I change log sono più pesanti, ma possono spiegare esattamente cosa è cambiato, campo per campo.

In ogni caso, l’UI dovrebbe poter mostrare tre valori per campo: la modifica dell’utente, il valore corrente del server e il punto di partenza condiviso.

Snapshot di record vs change log: scegli un approccio

Rendi eseguibili le regole di merge
Trasforma le regole di merge per campo in logica backend con un Business Process drag-and-drop.
Crea progetto

Quando sincronizzi form offline-first, puoi caricare l’intero record (uno snapshot) o una lista di operazioni (change log). Entrambi funzionano con Kotlin e SQLite, ma si comportano diversamente quando due persone modificano lo stesso record.

Opzione A: Snapshot dell’intero record

Con gli snapshot, ogni salvataggio scrive lo stato completo più recente (tutti i campi). Alla sincronizzazione invii il record più un numero di versione. Se il server vede che la versione è vecchia, hai un conflitto.

Questo è semplice da costruire e veloce da leggere, ma spesso genera conflitti più grandi del necessario. Se Utente A modifica il numero di telefono mentre Utente B modifica l’indirizzo, un approccio a snapshot può trattarlo come un unico grande scontro anche se le modifiche non si sovrappongono.

Opzione B: Change log (operazioni)

Con i change log memorizzi cosa è cambiato, non l’intero record. Ogni modifica locale diventa un’operazione che puoi riprodurre sopra lo stato server più recente.

Operazioni che sono spesso più facili da fondere:

  • Impostare il valore di un campo (set email a un nuovo valore)
  • Appendere una nota (aggiunge un nuovo elemento nota)
  • Aggiungere un tag (aggiunge un tag a un insieme)
  • Rimuovere un tag (rimuove un tag dall’insieme)
  • Segnare una checkbox come completata (set isDone true con un timestamp)

I log di operazioni possono ridurre i conflitti perché molte azioni non si sovrappongono. Aggiungere note raramente confligge con qualcun altro che aggiunge una nota diversa. Le aggiunte/rimozioni di tag possono fondersi come operazioni su set. Per campi a valore singolo, servono comunque regole per campo quando due modifiche competono.

Il compromesso è la complessità: ID stabili per le operazioni, ordinamento (sequenza locale e tempo server) e regole per operazioni che non commutano.

Pulizia: compacting dopo sincronizzazione riuscita

I change log crescono, quindi pianifica come ridurli.

Un approccio comune è la compattazione per record: una volta che tutte le operazioni fino a una certa versione server sono state acknowledgate, piega tutto in un nuovo snapshot e poi elimina le operazioni obsolete. Conserva solo una coda corta se hai bisogno di undo, audit o debug più semplice.

Flusso di sincronizzazione passo passo per Kotlin + SQLite

Costruisci full stack in un unico posto
Genera backend, web e app mobile pronte per la produzione da un unico workspace no-code.
Try now

Una buona strategia di sync riguarda soprattutto l’essere rigorosi su cosa invii e cosa accetti indietro. L’obiettivo è semplice: non sovrascrivere mai per errore dati più recenti e rendere i conflitti evidenti quando non si possono fondere in sicurezza.

Un flusso pratico:

  1. Scrivi ogni modifica prima su SQLite. Salva le modifiche in una transazione locale e marca il record con pending_sync = 1. Conserva local_updated_at e l’ultima server_version conosciuta.

  2. Invia una patch, non l’intero record. Quando torna la connettività, invia l’id del record più solo i campi che sono cambiati, insieme a expected_version.

  3. Lascia che il server rifiuti le versioni non corrispondenti. Se la versione corrente del server non corrisponde a expected_version, ritorna un payload di conflitto (record server, modifiche proposte e quali campi differiscono). Se le versioni corrispondono, applica la patch, incrementa la versione e restituisce il record aggiornato.

  4. Applica prima gli auto-merge, poi chiedi all’utente. Esegui le regole di merge a livello di campo. Tratta i campi sicuri (note) in modo diverso da campi sensibili (status, prezzo, assignee).

  5. Commit del risultato finale e pulizia dei flag pendenti. Che sia stato auto-fuso o risolto manualmente, scrivi il record finale nello SQLite, aggiorna server_version, imposta pending_sync = 0 e registra abbastanza dati di audit per spiegare cosa è successo in seguito.

Esempio: due commerciali modificano lo stesso ordine offline. Rep A cambia la data di consegna. Rep B cambia il numero di telefono del cliente. Con le patch, il server può accettare entrambe le modifiche senza problemi. Se entrambi cambiano la data di consegna, mostri una decisione chiara invece di costringere a reinserire tutto il record.

Mantieni la promessa dell’UI: "Salvato" dovrebbe significare salvato localmente. "Sincronizzato" è uno stato separato e esplicito.

Pattern UX per risolvere conflitti nei form

I conflitti dovrebbero essere l’eccezione, non il flusso normale. Inizia fondendo automaticamente ciò che è sicuro, poi chiedi all’utente solo quando una decisione è veramente necessaria.

Rendi i conflitti rari con predefiniti sicuri

Se due persone modificano campi diversi, fondi senza mostrare una modal. Mantieni entrambe le modifiche e mostra un piccolo messaggio "Aggiornato dopo la sincronizzazione".

Riserva i prompt per collisioni reali: lo stesso campo modificato su entrambi i lati, o una modifica che dipende da un altro campo (come stato più motivo dello stato).

Quando devi chiedere, rendi rapido il completamento

Una schermata di conflitto dovrebbe rispondere a due cose: cosa è cambiato e cosa verrà salvato. Confronta i valori affiancati: "La tua modifica", "La loro modifica" e "Risultato salvato". Se sono in conflitto solo due campi, non mostrare l’intero form. Vai direttamente a quei campi e tieni il resto in sola lettura.

Limita le azioni a ciò che serve davvero:

  • Mantieni la mia versione
  • Mantieni la loro versione
  • Modifica il risultato finale
  • Revisione campo per campo (solo se necessario)

I merge parziali sono dove l’UX diventa complicata. Evidenzia solo i campi in conflitto e etichetta chiaramente la fonte ("Tua" e "Loro"). Pre-seleziona l’opzione più sicura così l’utente può confermare e proseguire.

Imposta le aspettative così gli utenti non si sentano bloccati. Dì loro cosa succede se escono: per esempio, "Terremo la tua versione localmente e riproveremo la sincronizzazione più tardi" o "Questo record resterà in Needs review finché non scegli". Rendi quello stato visibile nella lista così i conflitti non si perdono.

Se stai costruendo questo flusso in AppMaster, lo stesso approccio UX vale: auto-merge dei campi sicuri prima, poi una revisione focalizzata solo quando campi specifici collidono.

Casi complessi: delete, duplicati e record “mancanti”

Mantieni piccole le revisioni dei conflitti
Crea schermate di revisione focalizzate che mostrino solo i campi che sono davvero in conflitto.
Try AppMaster

La maggior parte dei problemi di sync che sembrano casuali provengono da tre situazioni: qualcuno cancella mentre qualcun altro modifica, due dispositivi creano la “stessa” cosa offline, o un record scompare e poi riappare. Questi casi richiedono regole esplicite perché LWW spesso sorprende gli utenti.

Delete vs edit: chi vince?

Decidi se un delete ha più forza di una modifica. In molte app aziendali, il delete vince perché gli utenti si aspettano che un record rimosso rimanga rimosso.

Una regola pratica:

  • Se un record è stato cancellato su qualsiasi dispositivo, trattalo come cancellato ovunque, anche se ci sono modifiche successive.
  • Se i delete devono essere reversibili, trasforma il "delete" in uno stato archiviato invece che in una cancellazione hard.
  • Se arriva una modifica per un record cancellato, conserva la modifica nella cronologia per audit, ma non ripristinare il record.

Collisioni di creazione offline e bozze duplicate

I form offline-first spesso creano ID temporanei (come UUID) prima che il server assegni un ID finale. I duplicati succedono quando gli utenti creano due bozze per la stessa cosa reale (la stessa ricevuta, lo stesso ticket, lo stesso articolo).

Se hai una chiave naturale stabile (numero ricevuta, barcode, email più data), usala per rilevare collisioni. Se non ce l’hai, accetta che i duplicati possano succedere e fornisci un’opzione di merge semplice più tardi.

Suggerimento di implementazione: conserva sia local_id che server_id in SQLite. Quando il server risponde, scrivi una mappa e conservala almeno fino a quando sei sicuro che nessuna modifica in coda faccia ancora riferimento al local ID.

Prevenire la “resurrezione” dopo la sync

La resurrezione succede quando Dispositivo A cancella un record, ma Dispositivo B è offline e poi carica una copia vecchia come upsert, ricreandolo.

La soluzione è una tombstone. Invece di rimuovere la riga immediatamente, marcala come cancellata con deleted_at (spesso anche deleted_by e delete_version). Durante la sincronizzazione, tratta le tombstone come cambiamenti reali che possono sovrascrivere stati non cancellati più vecchi.

Decidi per quanto tempo conservare le tombstone. Se gli utenti possono restare offline per settimane, conservale più a lungo di quello. Pulisci solo quando sei sicuro che i dispositivi attivi si siano sincronizzati oltre la cancellazione.

Se supporti l’undo, tratta l’undo come un’altra modifica: cancella deleted_at e incrementa la versione.

Errori comuni che causano perdita di dati o frustrazione

Previeni sovrascritture silenziose
Configura aggiornamenti in stile patch e controlli di versione così i dati più nuovi non vengono sovrascritti silenziosamente.
Start building

Molte rotture della sincronizzazione derivano da piccole assunzioni che silenziosamente sovrascrivono dati validi.

Errore 1: Fidarsi dell’orario del dispositivo per ordinare le modifiche

I telefoni possono avere orologi sbagliati, i fusi orari cambiano e gli utenti possono impostare manualmente l’ora. Se ordini le modifiche per timestamp del dispositivo, prima o poi applicherai aggiornamenti nell’ordine sbagliato.

Preferisci versioni emesse dal server (una serverVersion monotona) e considera i timestamp client come solo per la visualizzazione. Se devi usare il tempo, aggiungi salvaguardie e riconcilia sul server.

Errore 2: LWW accidentale su campi sensibili

LWW sembra semplice finché non colpisce campi che non dovrebbero essere "vinti" da chi sincronizza più tardi. Stato, totali, approvazioni e assegnazioni di solito richiedono regole esplicite.

Una checklist di sicurezza per campi ad alto rischio:

  • Tratta le transizioni di stato come una macchina a stati, non come un campo libero.
  • Ricalcola i totali dalle righe: non unire i totali come numeri grezzi.
  • Per contatori, fai merge applicando delta, non scegliendo un vincitore.
  • Per proprietà o assegnatari, richiedi conferma esplicita sui conflitti.

Errore 3: Sovrascrivere valori server più nuovi con dati cache obsoleti

Questo succede quando il client modifica uno snapshot vecchio e poi carica l’intero record. Il server lo accetta e le modifiche server più recenti scompaiono.

Correggi la forma di ciò che invii: invia solo i campi cambiati (o un change log), più la versione base che hai modificato. Se la versione base è indietro, il server rifiuta o forza un merge.

Errore 4: Nessuna cronologia “chi ha cambiato cosa”

Quando i conflitti arrivano, gli utenti vogliono una risposta: cosa ho cambiato io e cosa ha cambiato l’altro? Senza identità dell’editor e una traccia per campo, la schermata di conflitto diventa congettura.

Conserva updatedBy, il tempo di aggiornamento server se disponibile, e almeno una cronologia per campo leggera.

Errore 5: UI di conflitto che forza il confronto dell’intero record

Far confrontare alle persone record interi è estenuante. La maggior parte dei conflitti riguarda solo 1–3 campi. Mostra solo i campi in conflitto, pre-seleziona l’opzione più sicura e lascia che l’utente accetti il resto automaticamente.

Se stai costruendo form in una piattaforma no-code come AppMaster, punta allo stesso risultato: risolvi i conflitti a livello di campo così gli utenti fanno una sola scelta chiara invece di scorrere tutto il form.

Checklist rapida e prossimi passi

Se vuoi che le modifiche offline diano sicurezza, tratta i conflitti come uno stato normale, non come un errore. I migliori risultati vengono da regole chiare, test ripetibili e un’UX che spiega cosa è successo in linguaggio semplice.

Prima di aggiungere altre funzionalità, assicurati che questi fondamentali siano solidi:

  • Per ogni tipo di record, assegna una regola di merge per campo (LWW, tieni max/min, append, union, o chiedi sempre).
  • Conserva una version controllata dal server più un updated_at che puoi validare durante la sincronizzazione.
  • Esegui un test a due dispositivi dove entrambi modificano lo stesso record offline e poi sincronizzano in entrambi gli ordini (A poi B, B poi A). L’esito dovrebbe essere prevedibile.
  • Testa i conflitti difficili: delete vs edit, e edit vs edit su campi diversi.
  • Rendi lo stato ovvio: mostra Synced, Pending upload e Needs review.

Prototipa il flusso completo end-to-end con un form reale, non una schermata demo. Usa uno scenario realistico: un tecnico aggiorna una nota su un telefono mentre un dispatcher modifica il titolo del lavoro su un tablet. Se toccano campi diversi, auto-merge e mostra un piccolo hint "Aggiornato da un altro dispositivo". Se toccano lo stesso campo, indirizza a una schermata di revisione semplice con due scelte e un’anteprima chiara.

Quando sei pronto a costruire l’app mobile completa e le API backend insieme, AppMaster (appmaster.io) può aiutare. Puoi modellare i dati, definire la logica di business e costruire UI web e native in un unico posto, quindi distribuire o esportare il codice sorgente una volta che le regole di sync sono solide.

FAQ

What is an offline sync conflict, in plain terms?

Un conflitto si verifica quando due dispositivi modificano lo stesso record mantenuto dal server mentre sono offline (o prima che uno dei due abbia sincronizzato), e poi il server vede che entrambi gli aggiornamenti si basano su una versione precedente. Il sistema deve quindi decidere quale valore finale usare per ciascun campo che differisce.

Which conflict strategy should I choose: last write wins, manual review, or field-level merge?

Inizia con field-level merge come predefinito per la maggior parte dei form aziendali, perché ruoli diversi spesso aggiornano campi diversi e così puoi conservare entrambe le modifiche senza disturbare nessuno. Usa manual review solo per campi che possono causare danni reali se si sbaglia (soldi, approvazioni, conformità). Usa last write wins solo per campi a basso rischio dove perdere una modifica precedente è accettabile.

When should the app ask the user to resolve a conflict?

Se due modifiche toccano campi diversi, di solito puoi fare il merge automaticamente e mantenere l’interfaccia silenziosa. Se due modifiche cambiano lo stesso campo con valori diversi, quel campo deve scatenare una decisione, perché qualsiasi scelta automatica può sorprendere qualcuno. Limita l’ambito della decisione mostrando solo i campi in conflitto, non l’intero form.

How do record versions prevent silent overwrites?

Tratta version come il contatore monotono del server per il record e richiedi al client di inviare un expected_version con ogni aggiornamento. Se la versione corrente del server non corrisponde, rifiuta con una risposta di conflitto invece di sovrascrivere. Questa singola regola previene la “perdita silenziosa” dei dati anche quando due dispositivi sincronizzano in ordini diversi.

What metadata should every synced SQLite table include?

Un minimo pratico è un id stabile, una version controllata dal server e updated_at/updated_by controllati dal server così puoi spiegare cosa è cambiato. Sul dispositivo, traccia se la riga è cambiata e in attesa di upload (per esempio pending_sync) e conserva l’ultima versione server sincronizzata. Senza queste informazioni non puoi rilevare i conflitti né mostrare una schermata di risoluzione utile.

Should I sync the whole record or only changed fields?

Invia solo i campi che sono cambiati (una patch) più la expected_version di base. Gli upload dell’intero record trasformano piccole modifiche non sovrapposte in conflitti inutili e aumentano il rischio di sovrascrivere un valore server più recente con dati locali obsoleti. Le patch rendono anche più chiaro quali campi richiedono regole di merge.

Is it better to store snapshots or a change log for offline edits?

Uno snapshot è più semplice: conservi l’ultimo record completo e lo confronti col server dopo. Un change log è più flessibile: memorizzi operazioni come “imposta campo” o “aggiungi nota” e le riproduci sullo stato server più recente, il che spesso si fonde meglio per note, tag e aggiornamenti additivi. Scegli snapshot per rapidità d’implementazione; scegli change log se i merge sono frequenti e hai bisogno di capire chiaramente “chi ha cambiato cosa”.

How should I handle delete vs edit conflicts?

Decidi in anticipo se il delete è più forte di una modifica, perché gli utenti si aspettano un comportamento coerente. Per molte app aziendali, un default sicuro è trattare i delete come “tombstone” (marcare come cancellato con deleted_at e una versione) così un upsert offline più vecchio non può riportare il record in vita. Se serve reversibilità, usa uno stato “archiviato” invece di un hard delete.

What are the most common mistakes that cause offline sync data loss?

Non ordinare le scritture critiche in base all’orario del dispositivo, perché gli orologi possono essere sbagliati e i fusi orari cambiano; usa le versioni del server per ordinare e verificare i conflitti. Evita LWW su campi sensibili come status, assignee e totali; fornisci regole esplicite o revisione manuale. Inoltre, non mostrare una schermata di conflitto sull’intero record quando solo uno o due campi sono in collisione, perché aumenta errori e frustrazione.

How can I implement a conflict-friendly UX when building with AppMaster?

Mantieni la promessa che “Salvato” significa salvato localmente e mostra uno stato separato per “Sincronizzato” così gli utenti capiscono cosa succede. Se costruisci questo flusso in AppMaster, mira alla stessa struttura: definisci regole di merge per campo come parte della logica di prodotto, fai il merge automatico dei campi sicuri e invia alle revisioni solo le vere collisioni fra campi. Testa con due dispositivi che modificano lo stesso record offline e sincronizzano in entrambi gli ordini per confermare che gli esiti siano prevedibili.

Facile da avviare
Creare qualcosa di straordinario

Sperimenta con AppMaster con un piano gratuito.
Quando sarai pronto potrai scegliere l'abbonamento appropriato.

Iniziare