DEV Community

Mustafa ERBAY
Mustafa ERBAY

Posted on • Originally published at mustafaerbay.com.tr

Mobile Offline-First Sync: Expectations vs. Realities

The Depths of Offline-First Synchronization in Mobile Applications

When it comes to mobile applications, user experience is everything. Providing a seamless experience even in situations with no or weak network connectivity is critical for standing out in today's competitive market. This is precisely where the "offline-first" architecture comes into play. However, this concept is often much more complex than a simple "store data locally and then synchronize" formula. Implementing offline-first in the real world requires a series of technical challenges and expectation management. In this post, I will delve into the expectations of offline-first synchronization in mobile applications, the challenges I've actually encountered, and the practical approaches I've adopted to overcome them.

At the core of this approach lies the idea that users can use the application with full functionality even when they are offline. Data is stored locally, operations are performed locally, and then these changes are synchronized with the server when a network connection is established. While this seems elegant at first glance, it brings with it complex issues such as conflict resolution, data consistency, and performance. In addressing these issues, I often had to adopt pragmatic approaches instead of ideal solutions.

Local Database Selection: The Cornerstones

At the heart of an offline-first architecture lies the choice of the local database. Which database we use directly impacts the application's performance, data integrity, and the complexity of the synchronization mechanism. Options typically include mobile-focused databases like SQLite, Realm, or Couchbase Lite, or mobile SDKs of more general-purpose NoSQL solutions. Each has its unique advantages and disadvantages.

For example, SQLite is a simple, lightweight relational database built into Android and iOS platforms. It is often sufficient for basic needs. However, it can fall short when it comes to complex data structures or real-time synchronization requirements. Realm, on the other hand, is a high-performance, easy-to-use object database specifically designed for mobile platforms. With its built-in synchronization engine, it offers an attractive option for offline-first scenarios. However, licensing costs and some ecosystem dependencies should be considered. NoSQL solutions like Couchbase Lite stand out with their flexible schema structure and powerful synchronization capabilities, but they may have a steeper learning curve.

Based on my experiences, in an enterprise ERP project, we initially started with SQLite. However, with real-time data updates on operator screens and an increasing data volume, we began to experience performance issues. At this point, migrating to Realm, which offers more advanced features and better synchronization capabilities, significantly improved the overall performance and user experience of the application. This migration process once again demonstrated how critical database selection is.

Synchronization Strategies: Conflicts and Solutions

The most challenging part of an offline-first architecture is ensuring data synchronization across multiple devices and resolving potential data conflicts. Users can update the same data on different devices both offline and online. In such cases, it's necessary to decide which change takes precedence. Several common synchronization strategies exist:

  • Last Write Wins (LWW): The simplest approach. The change that was made later is accepted. However, this strategy can lead to the loss of important data.
  • Vector Clocks: Provides a smarter conflict resolution by tracking the order of changes and which changes have "seen" each other.
  • Operational Transformation (OT): Resolves conflicts by making operations compatible with each other, similar to how it works in text editors. It is generally more complex.
  • Human Intervention: Presenting a user interface for conflicts that cannot be resolved automatically, allowing the user to make a manual decision.

In one project, I worked on both a local financial calculator application and a supply chain management system. For the financial calculator, the LWW strategy was sufficient for small updates users made offline because the risk of data loss was low. However, in the supply chain system, when multiple users made changes to the same order simultaneously, I found LWW to be inadequate for ensuring the order's currency and accuracy. In this situation, we queued each update with a timestamp and processed these changes on the server with more advanced logic. We adopted a kind of vector clock-like approach by maintaining a version number for each order item and incrementing it with every change. This ensured data integrity while also providing the user with feedback on the status of their changes.

ℹ️ Pragmatic Conflict Resolution

Automatic conflict resolution may not always be possible or desirable. In some cases, asking the user a question like "Who updated this data?" and presenting them with the most accurate information to let them decide might be the best solution. However, this can slow down the user experience, so it should be used carefully.

Performance Optimization: Local and Server-Side

Synchronization operations can lead to performance issues, especially with large datasets or under poor network conditions. Therefore, optimizing both on the local and server sides is vital. Locally, it's necessary to optimize database queries, prevent unnecessary data loading, and intelligently manage background synchronization. On the server side, efficient API designs, fast database queries, and a scalable infrastructure are essential.

While working on the mobile application for an e-commerce platform, the process of synchronizing users' order histories was incredibly slow. It took up to 5 minutes for a user with approximately 5000 orders to synchronize their data. Upon investigation, we found that separate queries were being made for each order, creating a domino effect in the database. To solve the problem, we first converted the database queries into a single batch query. Then, we added a "delta sync" mechanism to synchronize only data that had changed within the last 30 days. This reduced the synchronization time from 5 minutes to an average of 20 seconds.

On the server side, we continuously monitored the status of our APIs that processed incoming synchronization requests. We noticed that API response times increased, especially during peak hours. To address this, we optimized database indexes and started using a caching mechanism like Redis for frequently accessed data. These optimizations allowed our APIs to process thousands of requests per second more quickly.

💡 Background Synchronization

Background synchronization processes can drain the device's battery and negatively impact user experience. Therefore, it's important to intelligently adjust the frequency and timing of synchronization. Synchronizing when there's a network connection, when the battery level is high, and when the device is charging is the best approach.

Security: Data Integrity and Privacy

When an offline-first architecture is used in mobile applications, sensitive data is stored on the device. This raises additional concerns about data security and privacy. Encrypting the local database, protecting data transmitted over the network with HTTPS, and using strong authentication and authorization mechanisms on the server side are essential.

In a client project, we were developing a mobile application that contained users' personal health data. This data needed to be stored securely locally and encrypted when transferred to the server. We used the platform's provided encryption mechanisms for the local database. For example, on Android, we could encrypt the SQLite database with libraries like SQLCipher. During data transmission over the network, we ensured data security by making all API calls over HTTPS and properly validating server certificates.

Furthermore, we implemented role-based access control (RBAC) mechanisms to determine which data users could access and which operations they could perform. This was critical for preventing unauthorized access and protecting data privacy. It's also important to remember that temporary data generated during synchronization must be processed and deleted securely.

⚠️ Data Encryption Keys

The security of the keys used to encrypt the local database is extremely important. Storing these keys securely, for example, through the operating system's key management systems (KeyStore, Keychain), can prevent malicious actors from accessing local data.


# Example of KeyStore usage on Android
keytool -genkeypair -v -keystore my-release-key.jks -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

Real-World Expectations and Management

An offline-first architecture promises a seamless experience for users, but this doesn't always mean perfect synchronization. Properly managing user expectations is crucial to prevent disappointment. It's necessary to clearly indicate the application's synchronization status, inform the user about potential issues, and provide realistic estimates of how long synchronization might take.

While working on a mobile banking application, we enabled users to perform transactions offline. However, users were uncertain about when these transactions would be securely transferred to the server. To rectify this, we added a small status icon next to each transaction: "pending," "synchronizing," "completed," etc. Additionally, when the application opened or a network connection was established, we displayed notifications like "Last synced: 2 minutes ago." These simple UI/UX improvements increased user trust in the application and reduced uncertainty.

Furthermore, it's not just about the application "working," but also "working healthily." This means having strong debugging and monitoring capabilities. When an error occurs on the server side or a problem arises during synchronization, it's essential to set up logging and metric collection systems to quickly detect and resolve the issue. For example, collecting server logs in a central location using journald and preventing potential attack attempts with fail2ban increases the overall security and stability of the system.

🔥 Avoid Misleading the User

Misleadingly displaying synchronization status can cause users to believe their data is lost and completely lose trust in the application. Always be honest and transparent.

Conclusion: Embracing Complexity

Offline-first synchronization in mobile applications is certainly not a simple topic. A successful implementation requires careful database selection, intelligent synchronization strategies, comprehensive performance optimizations, and robust security measures. Most importantly, it involves properly managing user expectations and understanding the system's limitations. In my experience, the greatest success in this area comes not from promising flawless synchronization, but from building a realistic and robust system that offers transparency and trust to the user. Every challenge encountered on this journey is an opportunity for a better architecture and a more user-friendly product.

Top comments (0)