<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Neighbourhoodie</title>
    <description>The latest articles on DEV Community by Neighbourhoodie (@neighbourhoodie).</description>
    <link>https://dev.to/neighbourhoodie</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F9676%2F13bdfad0-be35-4061-bff9-eb8d56bb9027.png</url>
      <title>DEV Community: Neighbourhoodie</title>
      <link>https://dev.to/neighbourhoodie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/neighbourhoodie"/>
    <language>en</language>
    <item>
      <title>NH:STA S01E02 OpenPGP.js</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 01 Apr 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/nhsta-s01e02-openpgpjs-38cj</link>
      <guid>https://dev.to/neighbourhoodie/nhsta-s01e02-openpgpjs-38cj</guid>
      <description>&lt;p&gt;This post is part of a series on our work for the &lt;a href="https://www.sovereign.tech/" rel="noopener noreferrer"&gt;Sovereign Tech Agency&lt;/a&gt; (STA). Our first post in the series explains why and how we are contributing to various open source projects. &lt;/p&gt;

&lt;h2&gt;
  
  
  About the project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openpgpjs.org" rel="noopener noreferrer"&gt;OpenPGP.js&lt;/a&gt; is a pure, Open Source &lt;a href="https://en.wikipedia.org/wiki/Pretty_Good_Privacy#OpenPGP" rel="noopener noreferrer"&gt;OpenPGP&lt;/a&gt; implementation written in JavaScript. Its main use-case is enabling PGP workflows in web-based email systems, but as JavaScript is available on almost all devices these days, its utility is universal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our contributions
&lt;/h2&gt;

&lt;p&gt;We started out by &lt;strong&gt;introducing a fuzz testing suite&lt;/strong&gt; to the project. Fuzz testing is a form of unit testing, but instead of relying on manually crafted input and comparing it to the desired output, fuzz testing generates a near infinite number of permutations for input data to find rare implementation bugs. For security-related software, this is an important aspect of a complete automated testing suite.&lt;/p&gt;

&lt;p&gt;We then focussed on making the project &lt;strong&gt;more approachable for new contributors&lt;/strong&gt; by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;improving the documentation for first-time contributors&lt;/li&gt;
&lt;li&gt;adding a high-level description of the project’s architecture&lt;/li&gt;
&lt;li&gt;and improving the general contribution guidelines.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, we started work on &lt;strong&gt;migrating certain core modules from JavaScript to TypeScript&lt;/strong&gt;, to make crucial parts of the project more type-safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections from the team
&lt;/h2&gt;

&lt;p&gt;Here’s a short interview with Neighbourhoodie developer Alba Herrerías Ramírez, who runs our STF programme and worked on OpenPGP.js:&lt;/p&gt;

&lt;h3&gt;
  
  
  What was the most surprising thing working on this project?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Alba&lt;/strong&gt;: I’m not sure if it’s ‘surprising’ but something I found pleasant was their &lt;a href="https://github.com/openpgpjs/openpgpjs" rel="noopener noreferrer"&gt;user documentation&lt;/a&gt;, it’s great, I would like to see more projects paying this detail to docs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What was especially challenging about this project?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;OpenPGP.js have been planning to release v6 for a long time and our work got stuck in the middle (since they requested us to base our work in the v6 branch). We needed to accommodate the project’s timelines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, we could play to our strengths here and help a web-based project and we could build upon our work with Sequoia-PGP. There is lots to be done on the OpenPGP.js project and we hope we get another chance at helping them along.&lt;/p&gt;




&lt;p&gt;Find out more about the work we do by visiting the &lt;a href="https://neighbourhood.ie/blog" rel="noopener noreferrer"&gt;Neighbourhoodie Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>security</category>
      <category>privacy</category>
      <category>javascript</category>
    </item>
    <item>
      <title>NH:STA S01E01 Sequoia-PGP</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 25 Mar 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/nhsta-s01e01-sequoia-pgp-2em3</link>
      <guid>https://dev.to/neighbourhoodie/nhsta-s01e01-sequoia-pgp-2em3</guid>
      <description>&lt;p&gt;This post is part of a series on our work for the &lt;a href="https://www.sovereign.tech/" rel="noopener noreferrer"&gt;Sovereign Tech Agency&lt;/a&gt; (STA). Our first post in the series explains why and how we are contributing to various open source projects. &lt;/p&gt;

&lt;p&gt;In our first project with the STA — and first episode of the season — we take a closer look at sequoia-git and use our frontend skills to help the project. &lt;/p&gt;

&lt;h2&gt;
  
  
  About the project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://sequoia-pgp.org" rel="noopener noreferrer"&gt;Sequoia-PGP&lt;/a&gt; is an &lt;a href="https://www.openpgp.org" rel="noopener noreferrer"&gt;OpenPGP&lt;/a&gt; (Open Pretty Good Privacy) implementation in Rust. Its focus is on safety and correctness by using a memory-safe language. PGP has been the backbone for many encryption tasks for decades and this Rust implementation takes this ecosystem into the future.&lt;/p&gt;

&lt;p&gt;While work on the core library is progressing with sufficient speed, the lead maintainers are responsible for many ancillary tasks that are important to help the rest of the world to get on board. By relieving them of these tasks, we created space for the project to concentrate on what they do best: write security software without distractions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our contributions
&lt;/h2&gt;

&lt;p&gt;Our first task was to &lt;strong&gt;review the sequoia-git&lt;/strong&gt; subproject. Sequoia-git builds on top of git and allows you to cryptographically make sure that code changes you might incorporate into your software come from a trusted set of developers. We gave it a spin and presented the team with a comprehensive report where we believe the tool could be improved in terms of setup, first run, documentation, error handling and overall useability.&lt;/p&gt;

&lt;p&gt;Secondly, we developed from scratch a &lt;strong&gt;contributing guide&lt;/strong&gt; for Sequoia-PGP. Open Source projects thrive on the contributions they receive and constantly recruiting and onboarding new developers is a core requirement for any Open Source project maintainer. To make this easier on everybody, we ourselves became first-time contributors and wrote up everything we had to learn to get started. Now anyone who’d like to get started helping out the project can walk in our footsteps and won’t require as much direct help from the current maintainers, so they can focus on other important tasks.&lt;/p&gt;

&lt;p&gt;Next, we provided Sequoia-PGP with a modern &lt;strong&gt;Frontend Design and Reusable Styling&lt;/strong&gt;. Our prime goal here was to produce a system that was easy to maintain for many years by people who are not primarily web developers. This led us to eschew many of the modern best-practices designed for folks who do web development day in and day out, but these projects often come with a high learning curve and have to receive regular updates. By going back to web development foundations and minimal tooling, we achieved a modern, better maintainable and better looking website for Sequoia-PGP. This again removed significant time away from the core maintainers while making the project more approachable for newcomers.&lt;/p&gt;

&lt;p&gt;In addition to the successful completion of these milestones, &lt;strong&gt;this was also our very first project with the Sovereign Tech Agecy&lt;/strong&gt; and aside from working on the project itself, we also established the blueprint for all following projects. We thank the Sequoia-PGP team for their patience while we worked out the system as we went along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, we are very happy we managed to help Sequoia-PGP on a more sustainable path for their very important mission. We learned a lot about the OpenPGP ecosystem as a result, and as it turned out, not a moment too soon. Tune in next time when we cover our work on the OpenPGP.js project.&lt;/p&gt;




&lt;p&gt;Find out more about the work we do by visiting the &lt;a href="https://neighbourhood.ie/blog" rel="noopener noreferrer"&gt;Neighbourhoodie Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>rust</category>
      <category>security</category>
      <category>ux</category>
    </item>
    <item>
      <title>Neighbourhoodie and The Sovereign Tech Agency</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 18 Mar 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/neighbourhoodie-and-neighbourhoodie-the-sovereign-tech-agency-1ng2</link>
      <guid>https://dev.to/neighbourhoodie/neighbourhoodie-and-neighbourhoodie-the-sovereign-tech-agency-1ng2</guid>
      <description>&lt;p&gt;We’ve been doing something incredibly exciting for the last couple of years, that we’re getting around to sharing on DEV: &lt;strong&gt;Neighbourhoodie is an official &lt;em&gt;Implementation Partner&lt;/em&gt; of the &lt;a href="https://www.sovereign.tech/" rel="noopener noreferrer"&gt;Sovereign Tech Agency&lt;/a&gt; (STA)&lt;/strong&gt;, formerly the Sovereign Tech Fund, and their &lt;a href="https://www.sovereign.tech/programs/bug-resilience" rel="noopener noreferrer"&gt;Tech Resilience Program&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hold up, what does all that mean? Let’s back up a little:&lt;/p&gt;

&lt;p&gt;In 2021, &lt;a href="https://en.wikipedia.org/wiki/Log4Shell" rel="noopener noreferrer"&gt;a security issue (or vulnerability) was made public in the Log4j library&lt;/a&gt;. This is business as usual in the software world, security vulnerabilities are found every day, they are being reported, issues are fixed, new releases come out and then everyone can update.&lt;/p&gt;

&lt;p&gt;That time, things went a little different: the vulnerability was disclosed without anyone having a chance to fix it, let alone update their systems to the latest version.&lt;/p&gt;

&lt;p&gt;In addition, this vulnerability affected anyone running Log4j in a way that anyone anywhere can run unverified code on a target system. This is 10 out of 10 bad.&lt;/p&gt;

&lt;p&gt;Two more compounding factors made this issue even worse:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It turns out, Log4j is used everywhere. Not a day goes by where anyone doing any digital work is not using a system that uses Log4j. Small companies, big companies, governments, &lt;em&gt;everyone&lt;/em&gt; is using Log4j.&lt;/li&gt;
&lt;li&gt;Log4j was maintained by a very small team that was working part time and unpaid.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of many government agencies, the German Bundesamt für Sicherheit in der Informationstechnik (Federal agency for IT security) classified this as an “extremely critical threat situation”.&lt;/p&gt;

&lt;p&gt;In response to this, the German government founded the Sovereign Tech Agency to run programs that help avoid issues like this in the future.&lt;/p&gt;

&lt;p&gt;One of these programs is the &lt;em&gt;&lt;a href="https://www.sovereign.tech/programs/bug-resilience" rel="noopener noreferrer"&gt;Tech Resilience Program&lt;/a&gt;&lt;/em&gt;, formerly called the Bug Resilience Program:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Bug Resilience Program proactively increases the resilience of open source software infrastructure and empower small and medium-sized open source projects. The goal is to lower their risk of harboring bugs and improve their capacity to respond to bugs as they are discovered. The program provides services to OSS projects, such as helping projects deal with technical debt, working on known security issues, performing code security audits to reduce high-risk vulnerabilities, as well as offering a bug &amp;amp; fix bounty platform to discover, responsibly report, and fix bugs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So far so good, but where does Neighbourhoodie come into the picture?&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;As mentioned above, we are an &lt;em&gt;Implementation Partner&lt;/em&gt;. That means we work directly with Open Source projects and help them be more resilient in the face of security issues. We are collaborating with projects in the open and help them address their highest need issues. These vary widely from directly working on the code to improving processes, to internal documentation that helps onboard more folks, and sometimes it means taking on a bunch of chores to free up the core maintainers to focus on high-value work.&lt;/p&gt;

&lt;p&gt;Here’s an (abbreviated for clarity) outline of the kind of work we are doing with the projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Analysis&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;Organisational Preparation&lt;/li&gt;
&lt;li&gt;Technical Preparation

&lt;ol&gt;
&lt;li&gt;Review software dependencies&lt;/li&gt;
&lt;li&gt;Review project: Code &amp;amp; Tests, including CI&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Improvement&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;Testing

&lt;ol&gt;
&lt;li&gt;Add test coverage&lt;/li&gt;
&lt;li&gt;Introduce or expand automated testing&lt;/li&gt;
&lt;li&gt;Increase testing matrix&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Release Engineering

&lt;ol&gt;
&lt;li&gt;Stable versioning&lt;/li&gt;
&lt;li&gt;Automate releases&lt;/li&gt;
&lt;li&gt;Audit access control for release automation&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Software Development 

&lt;ol&gt;
&lt;li&gt;Help fix high-impact issues&lt;/li&gt;
&lt;li&gt; Help review outstanding contributions&lt;/li&gt;
&lt;li&gt; Improve documentation for first time contributors&lt;/li&gt;
&lt;li&gt; Improve contribution guidelines more generally&lt;/li&gt;
&lt;li&gt; Improve developer experience&lt;/li&gt;
&lt;li&gt; Help recruit more contributors&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;While all this is very abstract, we’ll share some of the concrete work we have done with actual projects in later blog posts.&lt;/p&gt;

&lt;p&gt;Neighbourhoodie are honoured to have already worked on diverse and essential projects, you can more about on our blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://neighbourhood.ie/blog/2024/08/07/nh-stf-s01e01-sequoia-pgp" rel="noopener noreferrer"&gt;NH:STA S01E01 Sequoia-PGP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neighbourhood.ie/blog/2024/09/11/nh-stf-s01e02-openpgpjs-copy" rel="noopener noreferrer"&gt;NH:STA S01E02 OpenPGP.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neighbourhood.ie/blog/2024/10/09/nh-stf-s01e03-yocto" rel="noopener noreferrer"&gt;NH:STA S01E03 Yocto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neighbourhood.ie/blog/2025/07/23/nh-stf-s01e04-systemd" rel="noopener noreferrer"&gt;NH:STA S01E04 systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neighbourhood.ie/blog/2025/08/05/nh-sta-s01e05-log4j" rel="noopener noreferrer"&gt;NH:STA S01E05 Log4j&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neighbourhood.ie/blog/2026/01/21/nh-sta-s01e06-reproducible-builds" rel="noopener noreferrer"&gt;NH:STA S01E06 Reproducible Builds&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll be publishing these stories here on DEV in the coming weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can you join?
&lt;/h2&gt;

&lt;p&gt;The Sovereign Tech Agency’s Tech Resilience Program is &lt;a href="https://www.sovereigntechfund.de/news/join-bug-resilience-program-vulnerability-management-support" rel="noopener noreferrer"&gt;open for your applications&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  We can also help you directly
&lt;/h2&gt;

&lt;p&gt;Many companies and products have crucial dependencies on small and potentially understaffed and underfunded Open Source projects. We can help identify and improve the ones that are important to your business. Our friendly sales team is happy to help. &lt;a href="https://neighbourhood.ie/call" rel="noopener noreferrer"&gt;Book a call with us today!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>security</category>
      <category>news</category>
    </item>
    <item>
      <title>How to Sync Anything: Building a Sync Engine from Scratch — Part 3</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 11 Mar 2026 14:08:40 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/how-to-sync-anything-building-a-sync-engine-from-scratch-part-3-1k51</link>
      <guid>https://dev.to/neighbourhoodie/how-to-sync-anything-building-a-sync-engine-from-scratch-part-3-1k51</guid>
      <description>&lt;p&gt;Last time we learned how to efficiently decide &lt;em&gt;what&lt;/em&gt; needs syncing. This time we will learn how to version our data.&lt;/p&gt;

&lt;p&gt;Let’s jump right in!&lt;/p&gt;

&lt;p&gt;In the example from part two, with stories updating after they’ve been created, and with apps being able to request a list of current stories at any time, we already saw how to deal with different &lt;em&gt;versions&lt;/em&gt; of a story in our calculation of a delta for an app to download.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwve6axysa647fi1v22c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwve6axysa647fi1v22c.png" alt="unique update sequence" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This third part explores different version schemes and discusses their respective trade-offs. Before we go into them, let’s briefly look at what kind of technique we are looking for.&lt;/p&gt;

&lt;p&gt;Given two objects with the same ID, we need to be able to tell which one came after the other, so we know which one is the &lt;em&gt;most recent&lt;/em&gt; one.&lt;/p&gt;

&lt;p&gt;If we have more than two, we need to be able to put them into an ordered list, so we know which ones come after others, and so we can know which one is the most recent, or &lt;em&gt;latest&lt;/em&gt;. And we need to be able to tell if a more recent version of an object is an ancestor of an older one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Increasing Integers
&lt;/h2&gt;

&lt;p&gt;The first and most natural idea for denoting versions of a document is increasing integers. That’s just a fancy way of saying “Version 1”, “Version 2”, “Version 3”, and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagjhztwt5zbvoupz59d1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagjhztwt5zbvoupz59d1.png" alt="Story objects with timestamps and a wall clock" width="486" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The advantage here is that these version numbers are very easy to understand for humans and computers alike.&lt;/p&gt;

&lt;p&gt;So what’s the problem?&lt;/p&gt;

&lt;p&gt;Imagine the client device and the server updating one of our story objects independently. Our story is at “Version 1”. The server then creates “Version 2” because that’s the next in the series.&lt;/p&gt;

&lt;p&gt;The client also creates a “Version 2”, but we have no way of knowing whether the contents of the versions are the same. When we try to synchronise the client and the server now, we run into trouble.&lt;/p&gt;

&lt;p&gt;We need to find something better to describe our versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timestamps
&lt;/h2&gt;

&lt;p&gt;The next common idea for denoting versions of a document is &lt;a href="https://en.wikipedia.org/wiki/Timestamp" rel="noopener noreferrer"&gt;a timestamp&lt;/a&gt;, a snapshot of a clock on some device, &lt;code&gt;Sat Jul 23 02:16:57 2005&lt;/code&gt;, or &lt;code&gt;12569537329&lt;/code&gt;. For example, we could have story objects with a property &lt;code&gt;updatedAt&lt;/code&gt; that we assign the timestamp of the last update to. And when we first create the object, we also record the current time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Aside: there are many formats for timestamps with properties ranging from easy to read for humans to easy to process for computers. Whichever you choose depends on a set of tradeoffs for your application and a discussion of the merits of one format against another is outside of the scope of this article. If you just want to pick one that’s generally good, go with &lt;a href="https://en.wikipedia.org/wiki/ISO_8601" rel="noopener noreferrer"&gt;ISO 8601&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fez8g15a64xowlknv49d8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fez8g15a64xowlknv49d8.png" alt="Story objects with timestamps and a wall clock" width="486" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we have two story objects with the same ID, we can compare their timestamps to figure out which one is the latest version of our story. As time has the convenient property of &lt;em&gt;monotonically increasing&lt;/em&gt;, in other words: clocks only ever go forward, and this will forever be true.&lt;/p&gt;

&lt;p&gt;Or will it? Before you go on, we’d like you to read this fantastic collection of &lt;a href="http://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time" rel="noopener noreferrer"&gt;falsehoods that programmers believe about time&lt;/a&gt; and &lt;a href="http://infiniteundo.com/post/25509354022/more-falsehoods-programmers-believe-about-time" rel="noopener noreferrer"&gt;the sequel&lt;/a&gt;. We’ll wait right here.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Hold music is playing&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Say we are editing our address book on our phone. First we add a contact’s new phone number. Phone numbers are icky to type so once we are done and compare it to where they wrote it down, we see we swapped two digits.&lt;/p&gt;

&lt;p&gt;Under the hood we recorded the current timestamp of the phone into our object versions, so we know which one was the latest. We fix the swapped digits quickly, no harm done. Or is there?&lt;/p&gt;

&lt;p&gt;As we’ve seen in &lt;em&gt;The Falsehoods&lt;/em&gt;, that is not always true. In fact, even companies with near infinite engineering and operations resources, &lt;a href="https://support.google.com/accounts/answer/185834?hl=en#sync" rel="noopener noreferrer"&gt;like Google, can run into trouble with this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a number of things that could conceivably happen, like a rogue time server telling the phone’s clock to adjust to an earlier time, or a daylight savings time switch (lucky they happen at night!); even Apple’s iPhones kept having issues with alarms set for January 1st for a while.&lt;/p&gt;

&lt;p&gt;In our example, this means the phone number with the typo will survive as the latest edit to our object, and not the correct one. This is not what we wanted.&lt;/p&gt;

&lt;p&gt;Whatever the exact scenario, this is plausible and has documented occurrences in small and large-scale systems: &lt;strong&gt;you can’t rely on timestamps to guarantee the order of two items&lt;/strong&gt;, even if the timestamps were generated on the same device as they lead to &lt;em&gt;data loss&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ok. How can we improve on timestamps?&lt;/p&gt;

&lt;p&gt;Before we find out, we need to introduce one more new concept: conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embracing Conflicts
&lt;/h2&gt;

&lt;p&gt;To illustrate conflicts we need to expand our example a little bit. Imagine our app is not just a consumption app for people who want to read our stories, but it is part of a content management system (CMS) where story authors can update their stories.&lt;/p&gt;

&lt;p&gt;Our system as designed so far already works in this case; we can have the server request latest story updates from the app of all authors, and it can then send updates to any apps of our readers.&lt;/p&gt;

&lt;p&gt;Now imagine this happening: an editor uses the CMS to edit a story by an author to fix a typo, while the author updates the same story with latest developments and we are using a timestamp to signify which version is the latest.&lt;/p&gt;

&lt;p&gt;Even &lt;em&gt;if&lt;/em&gt;  we now have two devices with perfectly synchronised clocks (which, as we’ve learned already, we can’t guarantee we have), this shows a larger problem: what happens if two people make changes to the same object on two different devices roughly at the same time?&lt;/p&gt;

&lt;p&gt;It doesn’t help that we know which one was updated after the other, either we pick the latest story developments and keep the typo, or vice versa. This is traditionally called a &lt;em&gt;conflict&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DUN. DUN. DUN&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you are anything like me (which you are probably not, but bear with me), you don’t like the idea of conflicts. I am, as they call it, &lt;em&gt;conflict averse&lt;/em&gt;. And while embracing conflicts might be a decent strategy in real life, in computing, we are usually trained that conflicts are bad. Very bad.&lt;/p&gt;

&lt;p&gt;That is until we start learning about &lt;em&gt;distributed systems&lt;/em&gt;. That’s a mighty term for a lot of ideas and concepts, but it usually boils down to: you have two or more computers connected by a network of some sort and you are trying to make it so some piece of data looks the same on both computers.&lt;/p&gt;

&lt;p&gt;Now the tricky part is this: either computer can fail at any time, and the network can have a multitude of failure scenarios (c.f. &lt;a href="https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing" rel="noopener noreferrer"&gt;The Fallacies of Networked Computing&lt;/a&gt;) that range from making the other computer appear very far away, or very busy, to turned off.&lt;/p&gt;

&lt;p&gt;Distributed systems is a large field with a lot of applications, but that must not scare us. On the one hand, we &lt;em&gt;do&lt;/em&gt; have a distributed system: two devices and a server, all connected over the internet.&lt;/p&gt;

&lt;p&gt;This fits the “two or more computers connected by a network” definition. But instead of introducing a whole new field of computing into our discussion, we’ll just learn a few best practices from distributed systems and then be on our way, so no worries!&lt;/p&gt;

&lt;p&gt;As opposed to single-machine computing, which is what we usually do, where we’ve learned that conflicts are a bad thing™, in distributed computing &lt;strong&gt;conflicts are a natural state of data&lt;/strong&gt;. That doesn’t mean we can just ignore them because they are “natural”. It means we cannot ignore the fact that these things exist, or try to build up an illusion that these things don’t exist. We have to &lt;strong&gt;embrace conflicts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We’ve already seen we can &lt;em&gt;create&lt;/em&gt; a conflict, now we can look how to &lt;em&gt;resolve&lt;/em&gt; one. Maybe in our scenario above, the typo fix is in the second paragraph and the new developments in the story are at the bottom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25osj52pwhxrds1trr5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25osj52pwhxrds1trr5u.png" alt="Article with two edits far away from each other" width="664" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we are storing a story paragraph by paragraph, for example, we could have a &lt;em&gt;conflict resolution&lt;/em&gt; procedure that checks if the two edits we have are in separate paragraphs, and if yes, updates our object with &lt;em&gt;both&lt;/em&gt; new versions of the respective paragraphs and then stores the result back into our object.&lt;/p&gt;

&lt;p&gt;This is commonly referred to as a &lt;em&gt;merge&lt;/em&gt;. If you’ve used &lt;em&gt;git&lt;/em&gt; or other source code version control systems (especially ones of the &lt;em&gt;distributed&lt;/em&gt; variety), you might have seen merge strategies that do exactly that.&lt;/p&gt;

&lt;p&gt;In this case, a computer can decide what the right final, non-conflicted version of our story is.&lt;/p&gt;

&lt;p&gt;But now imagine both author and editor fixed the typo, but the author decided to use a different word altogether, while the editor just corrected the spelling, and both are doing this at the same time.&lt;/p&gt;

&lt;p&gt;In that case, it is impossible for a computer to know which is the &lt;em&gt;correct&lt;/em&gt; version. We could apply a policy in &lt;em&gt;our&lt;/em&gt; app that in case of a conflict the author’s changes should take precedent. Or editors get the preferred treatment. Whichever it is, this can be a viable strategy for specific apps.&lt;/p&gt;

&lt;p&gt;As mentioned before, these are all examples, and we are trying to become experts in sync more generically. So we need a better solution.&lt;/p&gt;

&lt;p&gt;And here is a bit of a bummer: there are some types of conflicts that no computer can ever solve. We will need human intervention. There is no way around this, but at the end of the day, our brains are a lot more sophisticated than computers and &lt;em&gt;conflict resolution&lt;/em&gt; is where this plays out.&lt;/p&gt;

&lt;p&gt;Despite this, I hope you feel a little more comfortable with conflicts.&lt;/p&gt;

&lt;p&gt;As an added bonus, now we are equipped to learn how to improve on timestamps for versioning our objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector Clocks
&lt;/h2&gt;

&lt;p&gt;If you start searching for techniques to solve the issues with timestamps from a wall clock, you will eventually find a mention of &lt;a href="https://en.wikipedia.org/wiki/Vector_clock" rel="noopener noreferrer"&gt;vector clocks&lt;/a&gt;, for a variant, see &lt;a href="https://en.wikipedia.org/wiki/Lamport_timestamps" rel="noopener noreferrer"&gt;Lampert Timestamps&lt;/a&gt;. A vector clock also works with timestamps — though its source is not a wall clock, like we humans use, but a so-called &lt;em&gt;logical clock&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When using vector clocks as version specifiers, we not only send logical timestamps with our objects, but also the state of the logical clock, so that later, when it is &lt;em&gt;time&lt;/em&gt; (pun intended) to compare two objects, we can calculate which one came first. Great!&lt;/p&gt;

&lt;p&gt;Just one more thing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff5vo5eobyxyu8yx4dhux.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff5vo5eobyxyu8yx4dhux.jpg" alt="Lt. Frank Columbo, LAPD, Homicide" width="718" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember when we talked about the author and the editor of a story fixing a typo and adding to the story at the same time? We now know how to detect and how to resolve this conflict.&lt;/p&gt;

&lt;p&gt;But what if the typo fix both create result in the &lt;em&gt;same&lt;/em&gt; text? With timestamps or vector clocks, a conflict is still generated, and our subsequent merge procedure has got nothing to do. So we can handle this too, but since the resulting story is the same, wouldn’t it be nice to &lt;em&gt;not&lt;/em&gt; create a conflict here in the first place?&lt;/p&gt;

&lt;p&gt;I realise this example is a little bit contrived, but I hope by staying within our example project, we can see the problem here. Where the problem would &lt;em&gt;actually&lt;/em&gt; make our lives harder is when we don’t have a single sync server, but a cluster of sync servers, which we might need for scaling to higher and higher loads of our system.&lt;/p&gt;

&lt;p&gt;Explaining all this takes time, and this tutorial is already getting long, so we are going to skip the details of this. I hope you forgive me. If you don’t, I hope this gif of a squirrel makes up for it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmoxj43cexyvd3tnxf12.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmoxj43cexyvd3tnxf12.gif" alt="Squirrel eating" width="450" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Addressable Versions
&lt;/h2&gt;

&lt;p&gt;To solve the problem of same edits generating conflicts, we need to look at another technique: &lt;em&gt;Content Addressable Versions&lt;/em&gt;. The idea here is this: take the contents of an object, pass it through a &lt;a href="https://en.wikipedia.org/wiki/Hash_function" rel="noopener noreferrer"&gt;hash function&lt;/a&gt;, and use the resulting hash as the version. If we now make the same change on two devices, we don’t generate a conflict. Hooray!&lt;/p&gt;

&lt;p&gt;We have one old problem though: Content Addressable Versions are not ordered. There is no way to take two of those versions and know from their value which came before the other. This is unfortunate, but we can solve this.&lt;/p&gt;

&lt;p&gt;Instead of replacing the version with each object update, we keep an ordered list of versions along with the document. When we update the object, we put that Content Addressable Version at the top of the list. Then we can know which version came before another by traversing our list.&lt;/p&gt;

&lt;p&gt;Before this is generally useful, we must add one pragmatic trade-off. If we update our objects a lot, our list of versions gets very long, and we are storing a lot of data, and moving said data across devices, and at some point we are losing all the benefits we covered when we discussed &lt;em&gt;deltas&lt;/em&gt; above.&lt;/p&gt;

&lt;p&gt;As a result, we make this list a &lt;em&gt;bounded list&lt;/em&gt;, e.g., it can have at most “X” entries. We can even make this length adjustable depending on our application. Anything between 10 and 1000 is reasonable for general purpose applications, so we’ll have to play with our app to see what’s best.&lt;/p&gt;

&lt;p&gt;The trade-off here is avoiding unnecessary conflicts at the expense of a little more storage space, and we get to decide how much storage we want to spend for this benefit.&lt;/p&gt;

&lt;p&gt;You may think that the list of versions is rather clunky compared to a neat solution like Vector Clocks. But being able to avoid conflicts where possible becomes a priority when our server becomes a cluster of servers that all work independently to handle our application load and we want to present a consistent set of data to our users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing Conflicts Efficiently
&lt;/h2&gt;

&lt;p&gt;And then, &lt;em&gt;finally&lt;/em&gt;, one last thing. When we are storing our ordered list of versions, it looks like this, simplified to use numbers instead of our Content Addressable Versions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35u6w0jh0g9ddof7w00v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35u6w0jh0g9ddof7w00v.png" alt="Ordered list of versions" width="589" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have five versions; now we are creating a conflict. In order to signify we have two versions that are in conflict, we put them in a sub-list at &lt;em&gt;the top of our list&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xfv5un0u54tf7j16lb8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xfv5un0u54tf7j16lb8.png" alt="Conflicting versions" width="526" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now know that version “6” and version “5” are &lt;em&gt;in conflict&lt;/em&gt;. Now imagine instead of resolving the conflict first, we create another one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiaxwrrlpbavts523myhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiaxwrrlpbavts523myhu.png" alt="Nested versions" width="637" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have versions “7” and “6” in conflict, and that conflict being in conflict with version “5”. Heavy stuff!&lt;/p&gt;

&lt;p&gt;We are representing versions in a tree structure. It allows us to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;store conflicts efficiently, and&lt;/li&gt;
&lt;li&gt;resolve them recursively, so regardless of how many conflict we have, we can always get to a non-conflicted state eventually.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Putting it All Together
&lt;/h2&gt;

&lt;p&gt;Here is a summary of our algorithm, without the explanation of why we do all the things, summing up all of the above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There are two devices, A and B, that want to sync data from A to B.&lt;/li&gt;
&lt;li&gt;Read high watermark checkpoints from both devices, if they exist.&lt;/li&gt;
&lt;li&gt;Start reading the update sequence on A from the recorded high watermark or from the beginning, if none exists.

&lt;ul&gt;
&lt;li&gt;for each ID/version pair:&lt;/li&gt;
&lt;li&gt;is it a delete?

&lt;ul&gt;
&lt;li&gt;Yes: &lt;em&gt;store the delete locally&lt;/em&gt; (see below) on B.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;do we have the ID/version pair already locally on B?

&lt;ul&gt;
&lt;li&gt;No: fetch ID/version from A and &lt;em&gt;store it locally&lt;/em&gt; (see below) on B.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;store high watermark of current update sequence on A &lt;em&gt;and&lt;/em&gt; B.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Store it Locally:&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check version list to see if the new version is a direct successor of the currently latest version on B.

&lt;ul&gt;
&lt;li&gt;Yes: add the new version to the data store on B.&lt;/li&gt;
&lt;li&gt;No: create a conflict.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: I’m glossing over a few details here that involve tracking of deleted conflicts, which are outside of the scope of this tutorial.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! I hope you learned a lot about the pitfalls of synchronising data reliably and that you are not discouraged to tackle your own sync solutions based on the principles we’ve explored here.&lt;/p&gt;

&lt;p&gt;With all the knowledge you’ve gained here, I hope you feel empowered to build your own sync engine!&lt;/p&gt;

&lt;p&gt;Please leave any feedback in the comments &lt;a href="https://toot.berlin/@neighbourhoodie" rel="noopener noreferrer"&gt;on Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Without you knowing, I explained to you &lt;em&gt;the why and how&lt;/em&gt; of the &lt;a href="https://docs.couchdb.org/en/stable/replication/protocol.html" rel="noopener noreferrer"&gt;CouchDB Replication Protocol&lt;/a&gt;, which allows seamless peer-to-peer data sync between any number of peers, including all the scenarios we’ve explored above.&lt;/p&gt;

&lt;p&gt;The CouchDB Replication Protocol is implemented in CouchDB itself, so that covers our server component. Then there is the &lt;a href="https://pouchdb.com/" rel="noopener noreferrer"&gt;PouchDB&lt;/a&gt; project implementing the same protocol in JavaScript targeted at Browser and Node.js applications; that covers your clients and dev servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addendum
&lt;/h2&gt;

&lt;p&gt;There are two related technologies I’d be amiss to not mention: &lt;em&gt;Operational Transforms&lt;/em&gt; and &lt;em&gt;CRDTs&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Transforms
&lt;/h3&gt;

&lt;p&gt;If you’ve ever seen collaborative text editing on Etherpad, Google Docs, or similar, that is powered by a technology called &lt;a href="https://en.wikipedia.org/wiki/Operational_transformation" rel="noopener noreferrer"&gt;Operational Transforms&lt;/a&gt;. It is designed to let any number of people collaborate on text at the same time. It can deal with a certain level of network instability, but generally, it requires clients to be connected at all times. If you go and keep editing, your changes can be integrated later, but not indefinitely later.&lt;/p&gt;

&lt;p&gt;In addition, it is designed for text, not for generic objects, so for true offline capabilities of generic data objects, Operational Transforms are less useful. Check them out if you need a solution for mostly connected text.&lt;/p&gt;

&lt;h3&gt;
  
  
  CRDTs
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type" rel="noopener noreferrer"&gt;Conflict-Free Replicated Data Types&lt;/a&gt; or CRDTs are specialised data structures designed for use in distributed systems and they have a lot of the properties we’ve discussed in this tutorial, but most notably, they do &lt;em&gt;not&lt;/em&gt; have a concept of conflicts. How great is that?! Very, in fact.&lt;/p&gt;

&lt;p&gt;Alas, everything in programming is trade-offs, so what do we trade for being able to have conflict-free data structures? Well, they are specialised data structures, like sets and counters, and not generic object representations like JSON. So, we’ll have to buy into a whole world of these specialised data structures, and maybe we have a hard time mapping our application objects to them. If you think your application can benefit from CRDTs, I strongly encourage you to try them out, but &lt;a href="https://www.moment.dev/blog/lies-i-was-told-pt-1" rel="noopener noreferrer"&gt;be aware of the trade-offs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;An earlier version of this post used to be published on the now defunct hood.ie blog. That earlier version had received reviews from Naomi Slater, Katharina Hößel and Jake Archibald. My thanks to them.&lt;/small&gt;&lt;/p&gt;




&lt;p&gt;Discover more ways to work with CouchDB — the syncing database — on the &lt;a href="https://neighbourhood.ie/blog" rel="noopener noreferrer"&gt;Neighbourhoodie blog&lt;/a&gt;, where we regularly publish tips, guides and tutorials.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>distributedsystems</category>
      <category>couchdb</category>
    </item>
    <item>
      <title>It’s time for the 2025—2026 Annual Apache CouchDB User Survey!</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 04 Feb 2026 15:01:32 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/its-time-for-the-2025-2026-annual-apache-couchdb-user-survey-2l2g</link>
      <guid>https://dev.to/neighbourhoodie/its-time-for-the-2025-2026-annual-apache-couchdb-user-survey-2l2g</guid>
      <description>&lt;p&gt;CouchDB is an open source database made by a global team of volunteers. It’s shaped by its users and community through everything from contributions to project feedback — and now is the best time to get involved.&lt;/p&gt;

&lt;p&gt;📝 &lt;a href="https://docs.google.com/forms/d/e/1FAIpQLSf-40zTKB4B6lnlGVZs6pjwoS4tdazi27q095l7AyuDBi4oxQ/viewform?usp=sharing&amp;amp;ouid=117810026623171680518" rel="noopener noreferrer"&gt;Be a participant in the 2025—2026 Annual Apache CouchDB User Survey&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll have the opportunity to let us know what you use most, love most and need most from your CouchDB. &lt;/p&gt;

&lt;p&gt;We’ll also be back here when we’re ready to share the insights. If you’re curious what others have to say or want to learn what they have running alongside CouchDB in their stack, &lt;a href="https://buttondown.email/neighbourhoodie-news" rel="noopener noreferrer"&gt;sign up for our newsletter&lt;/a&gt; to get the results as they come out. &lt;/p&gt;

&lt;p&gt;We can’t wait to hear from you!&lt;/p&gt;

</description>
      <category>couchdb</category>
      <category>news</category>
      <category>community</category>
    </item>
    <item>
      <title>How to Sync Anything: Building a Sync Engine from Scratch — Part 2</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 19 Nov 2025 08:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/how-to-sync-anything-building-a-sync-engine-from-scratch-part-2-hjb</link>
      <guid>https://dev.to/neighbourhoodie/how-to-sync-anything-building-a-sync-engine-from-scratch-part-2-hjb</guid>
      <description>&lt;p&gt;In this part, we will learn how to efficiently find out what data needs to be synchronised.&lt;/p&gt;

&lt;p&gt;Say we have a news app that runs on mobile devices and a server that publishes new stories. Blogs and &lt;a href="https://en.wikipedia.org/wiki/RSS" rel="noopener noreferrer"&gt;RSS&lt;/a&gt; are good real-world examples.&lt;/p&gt;

&lt;p&gt;The scenario is this: our app starts for the first time and there are no stories available for the user to read on the device. So, the app asks the server to send the latest set of stories to the device. End result: our users get to read some stories.&lt;/p&gt;

&lt;p&gt;Later, when our apps starts for the second time, your app asks for the latest set of stories.&lt;/p&gt;

&lt;p&gt;And here is the first interesting bit: &lt;em&gt;we don’t want to send the app the stories it already has.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Four reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They are already on the device, so it’s redundant&lt;/li&gt;
&lt;li&gt;It costs us server processing time&lt;/li&gt;
&lt;li&gt;It costs our users bandwidth&lt;/li&gt;
&lt;li&gt;It adds response-time latency to the experience&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As a general rule of thumb, in user experience design: don’t let the user wait unnecessarily or they will get frustrated and stop using your app, your store, whatever.&lt;/p&gt;

&lt;p&gt;So what’s the solution?&lt;/p&gt;

&lt;p&gt;We need to request new stories from the server in a way that says “I’ve got all stories &lt;em&gt;up to this point&lt;/em&gt;. Give, give me &lt;em&gt;anything that’s new&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;We’ll call the difference between “all stories” and “stories that exist on the client” the &lt;em&gt;delta&lt;/em&gt; (i.e. difference).&lt;/p&gt;

&lt;p&gt;The delta is what we need to get from the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calculating the Delta
&lt;/h2&gt;

&lt;p&gt;To ask for the delta efficiently, we need two ingredients:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Our app needs to store its state somewhere, so it knows what stories it already has. We’ll call this the &lt;em&gt;high watermark&lt;/em&gt; (for reasons that will become clear soon).
In a native app, this could be stored in a device-local database or file. In a website or web app, this could live in browser storage systems like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" rel="noopener noreferrer"&gt;localStorage&lt;/a&gt;, or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API" rel="noopener noreferrer"&gt;IndexedDB&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Our server needs to look up the list of all stories, sorted by when they were published. It needs to be able to do do this efficiently. And it needs to be able to send back any range of stories, from any specified start date to the present moment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A naive implementation on the server side would be something equivalent to retrieving all stories from the database, sorting them by date in memory, before sending the result to the app. If the app sends a high watermark, the server would only send stories that come after the high watermark.&lt;/p&gt;

&lt;p&gt;While this definitely works, &lt;em&gt;it is not very efficient&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;All stories have to be loaded from the database, sorted and finally filtered to match the range the client is interested in.&lt;/p&gt;

&lt;p&gt;While a server with only a few apps requesting only a handful of stories can easily do this, we should be looking for optimisations here. Something that will work with large data sets.&lt;/p&gt;

&lt;p&gt;How? Traditionally, we would add an index to the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Index
&lt;/h2&gt;

&lt;p&gt;An index has two things going for it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Order:&lt;/em&gt; things will be stored in index order on disk, so reading things (e.g. stories sorted by publish date) in that order is very efficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Support for ranges:&lt;/em&gt; reading only part of an index from any point in the index range (e.g. all stories after a certain date) is very efficient.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe the database already has an auto-increment integer as a primary key. If so, each new story gets an ID integer that is one higher the previous ID.&lt;/p&gt;

&lt;p&gt;If this is the case, the server is already prepared and the app only needs to store the highest ID it gets from the server in the initial request. The app can then send that ID as the high watermark for subsequent requests.&lt;/p&gt;

&lt;p&gt;When the app receives the new batch of stories, it stores the new highest ID as the next high watermark, and so on.&lt;/p&gt;

&lt;p&gt;Here’s what this looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zcz9bwhljvpgyirfr9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zcz9bwhljvpgyirfr9p.png" alt="A server and client using high watermarks" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Updates
&lt;/h2&gt;

&lt;p&gt;Now, let’s imagine our stories are sometimes updated. This is fairly common. Typos need to be fixed, corrections posted, new story developments need to be added, and so on.&lt;/p&gt;

&lt;p&gt;In this case, using an auto-incrementing ID is not a good solution.&lt;/p&gt;

&lt;p&gt;Not only do new stories need to get a new ID, or more precisely a &lt;em&gt;higher&lt;/em&gt; ID, but also: &lt;em&gt;updates&lt;/em&gt; to a story need to get a higher ID, so that they are included in the calculation of the next delta.&lt;/p&gt;

&lt;p&gt;To solve this, we need a new table: &lt;em&gt;updates&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This updates table provides a secondary index that we use for recording updates only. If you add one record for every update, pointing back to the corresponding story that was updated, the auto-incrementing ID functions as an &lt;em&gt;update sequence&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now, instead of passing back the story ID as a high watermark, the client can pass back the last update sequence it got. (We have to tell the client the latest update sequence with every request for this to work.)&lt;/p&gt;

&lt;p&gt;When the server receives an update sequence from the client as the high watermark, all it needs to do is send back every story that corresponds to every update with a higher update sequence ID in the updates table.&lt;/p&gt;

&lt;p&gt;In this example, the client first receives five stories and the update sequence ID of “5”. When the client sends the “5” back as the high watermark, the server notices that there's an update record with ID “6” and sends back the only the corresponding story, which happens to be the story with ID “3”. The client also now gets the update sequence “6”, which is recorded locally as the high watermark.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbe620ln2v6itte6m3ilf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbe620ln2v6itte6m3ilf.png" alt="Update sequence being used as a high watermark" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, our app might choose to handle this information by adding an “updated” marker to the list of stories, so our user knows the story has been updated with new information, even if it was previously read.&lt;/p&gt;

&lt;p&gt;So far so good!&lt;/p&gt;

&lt;p&gt;But we need to handle one more case for updates: what happens when a story has been updated twice?&lt;/p&gt;

&lt;p&gt;This is how it would look in our current system:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsi7ab7hzr9l0h9j72tlf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsi7ab7hzr9l0h9j72tlf.png" alt="A server sending duplicate updates to the client" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This looks very similar to the previous image, with one difference: the delta includes story three twice. Thing is, we only really need the latest update. That previous update will be discarded by the client, so we're wasting server resources, bandwidth, and UX latency sending it.&lt;/p&gt;

&lt;p&gt;In our example scenario, this isn’t too bad. But imagine we're downloading thousands of objects with hundreds of updates.&lt;/p&gt;

&lt;p&gt;Not great.&lt;/p&gt;

&lt;p&gt;So what do we do about it?&lt;/p&gt;

&lt;p&gt;The update sequence index we’re using is called a &lt;em&gt;composite index&lt;/em&gt;. This means more than one data item defines the index range and how it is sorted. In our case, we have an auto-increasing change ID plus a static story ID.&lt;/p&gt;

&lt;p&gt;We need to make one more change to our composite index to solve this multiple update problem.&lt;/p&gt;

&lt;p&gt;Let’s make it so that the story ID column in the updates table is unique. Every time we try to write to that table, if we see there’s an existing entry with the story ID we’re about to record a change for, we delete the old row.&lt;/p&gt;

&lt;p&gt;If we do this, there will only ever be one update record per story, and it will always correspond to the latest update.&lt;/p&gt;

&lt;p&gt;Here’s what that looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1g6m6ysnia102om7yodh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1g6m6ysnia102om7yodh.png" alt="Unique Update Sequence" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What happened here?&lt;/p&gt;

&lt;p&gt;Between the first and second requests, two changes were made to story three. Because the story ID column on the updates table is unique, we only have the final update recorded, which is why the update with ID “6” is missing.&lt;/p&gt;

&lt;p&gt;The end result? Our client only receives the final update for story three.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deletes
&lt;/h2&gt;

&lt;p&gt;There’s one last thing before we move on: deletes.&lt;/p&gt;

&lt;p&gt;In our app, we want to treat deletes as updates, so that deletes can be sent to the client like any other sort of change.&lt;/p&gt;

&lt;p&gt;Let’s take the same example we’ve already been using and instead imagine that between the first and second client requests, story four is deleted from the server.&lt;/p&gt;

&lt;p&gt;In this case, we’d want to record this deletion in the updates table, along with an update ID. We can then send this information back to the client as if it were a new story, updating the high watermark as we do so.&lt;/p&gt;

&lt;p&gt;Here’s what that would look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajenartq4im8o95xqtc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajenartq4im8o95xqtc5.png" alt="Handling Deletes" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, the app has several choices. The simplest is probably just to delete the story locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Road to CRUD
&lt;/h2&gt;

&lt;p&gt;Congratulations! Now we know how to communicate new stories, story updates, and story deletions to our app. In other words, we can sync Create, Read, Update, and Delete (&lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete" rel="noopener noreferrer"&gt;CRUD&lt;/a&gt;) operations.&lt;/p&gt;

&lt;p&gt;And here’s a cool thing: we’ve done this generically enough that a story can be any sort of object we might want.&lt;/p&gt;

&lt;p&gt;In this post, we’ve spoken about different versions of the same object. For example, using &lt;code&gt;3&lt;/code&gt;, &lt;code&gt;3*&lt;/code&gt;, and &lt;code&gt;3**&lt;/code&gt; to refer to the first, second, and third versions of story three. We’re also talking about versions when we talk about the deleted and non-deleted version of story &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This concept — versions — is very important. So we’ll take a closer look at that in post three of this three part miniseries.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;An earlier version of this post used to be published on the now defunct hood.ie blog. That earlier version had received reviews from Naomi Slater, Katharina Hößel and Jake Archibald, tef, and James. My thanks to them.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Discover more ways to work with CouchDB — the syncing database — on the &lt;a href="https://neighbourhood.ie/blog?pk_campaign=dev&amp;amp;pk_kwd=sync-2" rel="noopener noreferrer"&gt;Neighbourhoodie blog&lt;/a&gt;, where we regularly publish tips, guides and tutorials.&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>architecture</category>
      <category>couchdb</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>How to Sync Anything: Building a Sync Engine from Scratch — Part 1</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 12 Nov 2025 08:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/how-to-sync-anything-building-a-sync-engine-from-scratch-part-1-3eal</link>
      <guid>https://dev.to/neighbourhoodie/how-to-sync-anything-building-a-sync-engine-from-scratch-part-1-3eal</guid>
      <description>&lt;p&gt;There’s an old saying I paraphrased in this by now ancient tweet[sic]:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdj9nw0jfxdstnfwfe1fw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdj9nw0jfxdstnfwfe1fw.png" alt="“Friends don’t let friends build their own {CRYPTO, SYNC, DATABASE}.” — @janl" width="471" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;“Friends don’t let friends build their own {CRYPTO, SYNC, DATABASE}.” — &lt;a class="mentioned-user" href="https://dev.to/janl"&gt;@janl&lt;/a&gt; on September 24th, 2014&lt;/p&gt;

&lt;p&gt;What do I mean by that?&lt;/p&gt;

&lt;p&gt;Well, it’s very hard to get these things right. Additionally, not getting them right will mean a lot of unhappy developers and end-users. In other words, these things are best left to the experts. &lt;/p&gt;

&lt;p&gt;However.&lt;/p&gt;

&lt;p&gt;Brought to its logical conclusion, we’ll end up with a situation where nobody is doing crypto, sync or databases anymore, because no new people learn these things. So there are exceptions.&lt;/p&gt;

&lt;p&gt;You &lt;strong&gt;can be an exception&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Let’s become experts in one of these things! Specifically, for the purposes of this series of posts: &lt;strong&gt;sync&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Or, to put it another way: let’s look at a bunch of different problems and solutions that relate to making data available offline.&lt;/p&gt;

&lt;p&gt;I’m picking a specific field (web frontend / backend sync) by way of example, but know that the same concepts apply to any two or more sites you want to synchronise.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;p&gt;When I first wrote this article, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps" rel="noopener noreferrer"&gt;Progressive Web Apps&lt;/a&gt; (PWAs) were &lt;em&gt;just&lt;/em&gt; being announced as maybe becoming a thing in the future.&lt;/p&gt;

&lt;p&gt;That future has long since arrived. What has not arrived is the logical next step that comes after caching your static assets: how to make your data available when a web app is not connected to a server.&lt;/p&gt;

&lt;p&gt;You already know how to store your website’s assets in a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="noopener noreferrer"&gt;ServiceWorker&lt;/a&gt; cache. You already have some idea about how to store server-delivered content in &lt;a href="http://www.pocketjavascript.com/blog/2015/11/23/introducing-pokedex-org" rel="noopener noreferrer"&gt;IndexedDB&lt;/a&gt;. And you might know how to locally store user-entered data (think an address book, note taking, favourites, and so on) in IndexedDB or localStorage so you can push it to the server when a network connection becomes available.&lt;/p&gt;

&lt;p&gt;We can already see a few different scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;server always pushes (news)&lt;/li&gt;
&lt;li&gt;client always pushes (notes)&lt;/li&gt;
&lt;li&gt;both push (social, email, or multi-device access of individual notes, and all forms of group data sharing.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s have a look at the different techniques required to make this all work. We’ll see which applies to which use-case while we go through everything, step-by-step.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scene: Background Sync
&lt;/h2&gt;

&lt;p&gt;In his &lt;a href="https://www.youtube.com/watch?v=cmGr0RszHc8" rel="noopener noreferrer"&gt;introductory talk about PWAs&lt;/a&gt;, Jake Archibald shows an example of a chat application. &lt;a href="https://youtu.be/cmGr0RszHc8?t=2390" rel="noopener noreferrer"&gt;Towards the end&lt;/a&gt;, he uses the Background Sync API to send new messages “later”, whenever the browser thinks it has an actual internet connection (as opposed to having a network but not thinking there’s an internet connection).&lt;/p&gt;

&lt;p&gt;This is a great UX experience: the interaction is &lt;em&gt;done&lt;/em&gt; as far as the user is concerned. There is still an indicator that the message hasn’t reached the recipient yet, but there is no need to keep the user waiting for any network operations.&lt;/p&gt;

&lt;p&gt;In the same talk, Jake explained that the browser (and sometimes mobile operating system) mechanics that determine whether a device is online are not very useful. That’s because they only cover the connection from the device to the wifi router or cell tower. But there are a lot of steps between there and the final web server. For example: ISP routers, transparent proxies, satellite uplinks, just to name a few.&lt;/p&gt;

&lt;p&gt;Now imagine we are sending a message within a &lt;em&gt;Background Sync&lt;/em&gt; event as shown in Jake’s talk, but we’re on a fast train that goes through a bunch of tunnels in fast succession. Or we are on a conference or hotel wifi. Or we’re a large event with thousands of other people. &lt;/p&gt;

&lt;p&gt;Our phone might get a request/response going, and as a result, wakes up Background Sync and tries to send our message. But by the time the message gets sent, we don’t get any requests going because the network became unavailable again. Background Sync will then re-try at a later time. Which is great, and exactly what we want. We don’t have to worry about whether the message is sent, because we know it will be, eventually.&lt;/p&gt;

&lt;p&gt;Now. Let’s look behind the scenes. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Cycle of (the) Web
&lt;/h3&gt;

&lt;p&gt;Even though there are many hops at which things can go wrong in a HTTP request/response cycle, there are generally two parts: the request and the response. &lt;/p&gt;

&lt;p&gt;Each part can fail, so we have to account for the following scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the request fails&lt;/li&gt;
&lt;li&gt;the request makes it, but the response fails&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Scenario one is neatly covered by Background Sync, but what about scenario two?&lt;/p&gt;

&lt;p&gt;Imagine we are sending a message. The server accepts it and sends it onwards to the final recipient. Then, the server responds to the device, saying it handled the message correctly. &lt;/p&gt;

&lt;p&gt;If that response fails, Background Sync will consider the message sending request as failed and will re-try it later. At that time, the server accepts the message and sends it to the recipient again. And now we’ve created a mess, because the recipient is left wondering why there are two messages that are exactly the same. &lt;/p&gt;

&lt;p&gt;And if we are really unlucky, our recipient will get an infinite stream of the same message because the same problem is happening over and over and over again.&lt;/p&gt;

&lt;p&gt;Let’s see how we can avoid this. &lt;/p&gt;

&lt;p&gt;There are solutions for this exact problem that we can employ in the server portion of our app. But we’ll get to that later. For now, let’s look at how to solve this &lt;em&gt;more generically&lt;/em&gt;, so that we can solve the same problem when it comes up in very different contexts, not just when sending messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity
&lt;/h2&gt;

&lt;p&gt;In programming, we generally work with bits of data wrapped up in objects. If we want to be able to refer to an object at a later time, we must be able to reference it unambiguously. Sometimes that means giving something a name (like an auto incrementing number) and sometimes we can derive an identity from a number of uniquely identifying properties of the object.&lt;/p&gt;

&lt;p&gt;For example: in an address book, assuming no two people have the same name (a bad assumption, but we’ll get to this shortly), the identity (or ID) of an object could be derived from the combination of the first name and the last name. That would be an example of what is known in database circles as a &lt;a href="http://www.databasesoup.com/2015/03/primary-keyvil-reprised.html" rel="noopener noreferrer"&gt;natural key&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The advantage of a natural key is that you don’t need to store any extra data on the object to be able to refer to it later. It also means your data is easier to de-duplicate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Natural Key Disadvantages
&lt;/h3&gt;

&lt;p&gt;There are two disadvantages to using natural keys: changes in the natural key and the need for uniqueness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Changes
&lt;/h3&gt;

&lt;p&gt;Say your natural key is somebody’s email address. Email addresses change, and you need to be able to deal with this change. &lt;/p&gt;

&lt;p&gt;For example, if you update someone’s email address, but another object was using that as the natural key to refer to your object, that second object will need to update its reference. &lt;/p&gt;

&lt;p&gt;While this type of change might be infrequent, other natural keys can change more frequently.&lt;/p&gt;

&lt;p&gt;This is opposed to an &lt;em&gt;opaque key&lt;/em&gt;, or &lt;em&gt;surrogate key&lt;/em&gt;, that is a number (like an account number, a social security number, a phone number) or string of random characters (e.g. a &lt;a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" rel="noopener noreferrer"&gt;UUID&lt;/a&gt;). Every time you’ve seen an auto incrementing ID column in an SQL database, that is a surrogate key.&lt;/p&gt;

&lt;h4&gt;
  
  
  Uniqueness
&lt;/h4&gt;

&lt;p&gt;In our address book example, when your natural key is derived from  last name and the first name, we may hit a problem. What if you know two people named Jane Smith? Our natural key is not unique, so if we try to look up “Jane Smith”, we can’t be sure which object to return.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surrogate Key Advantages
&lt;/h3&gt;

&lt;p&gt;Surrogate keys have the advantage of being agnostic to any changes in our objects’ data. &lt;/p&gt;

&lt;p&gt;The disadvantage is that they are opaque, so there’s no natural relationship between an object’s ID and its data. The ID 43135 doesn’t tell you a whole lot about a user record for Jane Smith. &lt;/p&gt;

&lt;p&gt;While these IDs are usually only used by computers, sometimes they leak out into the real world. Sometimes we even expect people to remember them and manipulate them. Not ideal. In addition, they make debugging and logging harder, as devs are now forced to map IDs to objects’ natural data to see anything useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Forwards
&lt;/h2&gt;

&lt;p&gt;Since we are looking at how to make &lt;em&gt;data available offline&lt;/em&gt;, and since that means storing data on an end-user device &lt;em&gt;and&lt;/em&gt; a server, we have multiple copies of all our data. So we need to be sure that any operations can unambiguously reference that data. &lt;/p&gt;

&lt;p&gt;So, when creating a new object that we give it a unique ID. And we can’t guarantee this with natural keys. A new Jane Smith could be added on the server and the user’s device while they can’t talk to each other because one is offline. That would spell all sorts of trouble for us.&lt;/p&gt;

&lt;p&gt;Unless any of our data lends itself to be a natural key without the disadvantages, we use surrogate keys.&lt;/p&gt;

&lt;p&gt;Specifically we use UUIDs, because they have the convenient property that even though you still can’t guarantee the uniqueness of natural data on disconnected devices, the chance of assigning the same UUID to different objects on two or more devices is so unlikely, we practically don’t even have to consider it a possibility.&lt;/p&gt;

&lt;p&gt;With that sorted, we have a new problem. We have multiple sets of data, which may or may not diverge from each other in various ways. In my next post, I’ll show you how to figure out what’s changed, what needs syncing, and in what direction.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;An earlier version of this post used to be published on the now defunct hood.ie blog. That earlier version had received reviews from Naomi Slater, Katharina Hößel and Jake Archibald. My thanks to them.&lt;/small&gt;&lt;/p&gt;




&lt;p&gt;Discover more ways to work with CouchDB — the syncing database — on the &lt;a href="https://neighbourhood.ie/blog?pk_campaign=dev&amp;amp;pk_kwd=sync-1" rel="noopener noreferrer"&gt;Neighbourhoodie blog&lt;/a&gt;, where we regularly publish tips, guides and tutorials.&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>architecture</category>
      <category>couchdb</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>How to Sync Anything</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 05 Nov 2025 13:40:23 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/how-to-sync-anything-54p1</link>
      <guid>https://dev.to/neighbourhoodie/how-to-sync-anything-54p1</guid>
      <description>&lt;p&gt;In this article I’ll discuss a common naive solution to replication, why it doesn’t work, and what the building blocks of a good solution look like. Having established this theoretical framework, my next article will look at how CouchDB provides many of those building blocks such that replicating from it into any other system is relatively painless.&lt;/p&gt;

&lt;p&gt;Something I have seen happen surprisingly often in my career as a developer is that teams end up having to implement replication, but they don’t acknowledge that this is what they’re doing, and so end up with ad-hoc solutions that don’t really work in production. When I’ve worked at product companies this usually takes the form of integrating internal systems, such as caching, search indexing, or making incremental updates in single-page apps. Sometimes it means having to integrate with external services, for example copying a company’s user profiles into a newly adopted CRM system and keeping them up to date going forward.&lt;/p&gt;

&lt;p&gt;As a consultant in the public sector, I’ve seen many &lt;strong&gt;projects whose entire purpose was to integrate several existing systems&lt;/strong&gt; and make sure they all agree on the exact state of their data. For example, the permissions for accessing casework documents are driven by roles stored in the HR department’s Active Directory. Or, some accounting information needs to be saved in two places at once because two organisations got merged and are in the process of splicing their systems together. Or, in the tech infrastructure world, you might have a complicated build pipeline involving generation of artifacts from many different input sources and you want it to react to upstream changes promptly.&lt;/p&gt;

&lt;p&gt;All of these are, at heart, &lt;em&gt;replication&lt;/em&gt; problems: the goal is to make some target data store (a cache, an index, an external HR system, a package repository) in some sense mirror the state of a source data store. I am using the term “data store” in the loosest possible sense here, to mean any stateful representation of some of the information in your system. Often these will be honest-to-god &lt;em&gt;databases&lt;/em&gt; — an RDBMS, a document store, etc. — but you really ought to think of any part of your system that accumulates state in this way.&lt;/p&gt;

&lt;p&gt;There are many different techniques for solving this problem, and one’s choice of solution is often constrained by the fact that many of the products people deploy to store and distribute their system’s data are not designed to make replication easy to implement or reliable to operate, especially when numerous heterogeneous systems need to be integrated. A lot of the more “obvious” solutions are riddled with pitfalls that stem from the fact it is fundamentally quite hard to keep two stores in sync when one or both of them keep changing, when there’s no globally agreed ordering of events, and when updates can fail, get re-ordered or lost. Many of the problems I’ve seen in production happen because of a naive approach to these problems, or because engineers ignore them entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Easy Solution: ETL
&lt;/h2&gt;

&lt;p&gt;Before getting into the details, I’ll quickly mention one solution that works really well for certain situations. Given that updating the target system incrementally from the source is hard to do correctly, the simplest possible solution is to not bother doing that at all. Instead, you rebuild the target from scratch periodically. You locate all the data of interest in the source, pull it out, transform it as necessary, and write it into the target, which starts from a blank state on each run.&lt;/p&gt;

&lt;p&gt;This process is generally known as &lt;a href="https://en.wikipedia.org/wiki/Extract,_transform,_load" rel="noopener noreferrer"&gt;extract, transform, load&lt;/a&gt; (ETL) and is widely used in analytics and data warehousing. It is very easy to make it correctly mirror the state of the source, because the target does not retain state for very long. If you find a bug in the data copying logic, you fix it, deploy the fix, and the state gets corrected the next time the process runs, because you’re building a fresh copy of your data every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pros and Cons of ETL
&lt;/h3&gt;

&lt;p&gt;The downside of this approach is that it’s very slow, because you have to scan all your source data every time. &lt;strong&gt;In fact it gets slower as your system grows&lt;/strong&gt;, which can produce costs that don’t scale in the same way as your revenue sources. It also has bad consistency; if the target is rebuilt nightly or weekly, then it is never fully in sync with the source, and may even be internally inconsistent as your source data changes while it’s being copied.&lt;/p&gt;

&lt;p&gt;For some applications, this is not a problem. Many analytics use cases do not benefit from real-time access to data and may even give worse results in this mode. They can also make use of cheaper archival storage technology and specialised processing tools so that their operational cost doesn’t grow too badly as your source data grows.&lt;/p&gt;

&lt;p&gt;There is also an operational benefit that comes from decoupling. The ETL approach typically doesn’t need custom logic built into application code to make it work. Application developers can get on with building features, while a separate team maintains the system the extracts what they want from the application database, as long as the data extraction does not cause excessive load and interfere with end user traffic. This lets both groups operate quite independently without causing one another friction.&lt;/p&gt;

&lt;p&gt;So, if you don’t mind the target being slightly stale, and taking a long time to update, ETL is a great solution. But what happens when that’s not an option?&lt;/p&gt;

&lt;h2&gt;
  
  
  Ad-Hoc Replication
&lt;/h2&gt;

&lt;p&gt;ETL may be conceptually very simple, but it has some major downsides. It cannot maintain real-time consistency with the source system, and it’s also extremely wasteful to scan through all your data on every rebuild, when most of it will not have changed since the last run. To address both these issues, developers will often want a more incremental approach to keeping the target up to date.&lt;/p&gt;

&lt;p&gt;This is where things typically start to go wrong. To make incremental sync work correctly, it is usually necessary to think about the big picture of how you want the source and target to interact and design or adopt a protocol that fits all these needs. However, many of these replication projects are developed in an ad-hoc way, with little bits of code being added to handle particular scenarios, and the interactions and failure modes of these small changes are never looked at holistically.&lt;/p&gt;

&lt;h3&gt;
  
  
  The “Waiting for Changes” Approach
&lt;/h3&gt;

&lt;p&gt;A very common approach to ad-hoc replication, especially when used to implement caching or search indexing, is to put &lt;strong&gt;hooks or event listeners&lt;/strong&gt; into the application that react to changes in the data, and forward those changes to the target system. Those hooks may be put into the data model, so you can be sure that any code path that changes a record will invoke the hooks. However, this means it’s impossible to invoke the code in the data model without causing these side effects to the target system, which might be undesirable, especially for testing. Conversely, if the hooks are put into the application’s request handling tier, this keeps the data layer uncoupled from the target system, but it’s much harder to make sure you cover every code path that might result in a record being changed. In some frameworks, it’s not uncommon for developers to debug and fix production issues by invoking the data model through an interactive shell, and any changes made this way may bypass any application logic built atop the model.&lt;/p&gt;

&lt;p&gt;This is the first source of inconsistency in these systems: &lt;strong&gt;the method of detecting changes to the source data is itself lossy&lt;/strong&gt;. Because of this, there is no way of knowing whether any given record in the source system is accurately mirrored in the target without scanning the entire data set, i.e. without ending up with an ETL implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Happens in a Rollback?
&lt;/h3&gt;

&lt;p&gt;The second major source of inconsistency comes from what those event hooks actually do with the change they are observing in the source data. When writing to the target system, they might attempt to relay the full state of the source record, or just the fields that changed in the source record, depending on the data models of the source and target and what facilities their framework provides. The latter assumes that the target record was already in sync with the previous state of the source record, which might not be the case, especially when other failure modes are accounted for, and this can result in lost updates. Conversely, the event hook might be implemented in such a way that it can be activated by changes in the model that have been sent to the database, but are part of a transaction that has not yet committed. If the transaction ends up being rolled back, then you end up with uncommitted data in the target system. This can be especially hard to clean up because the target state results from source state that should never have been recorded and will never be explicitly deleted.&lt;/p&gt;

&lt;h3&gt;
  
  
  More Ad-Hoc Replication Inconsistencies…
&lt;/h3&gt;

&lt;p&gt;Because the source and target are often distinct systems, the target may be unavailable when the source is changed, so the event needs to be retried somehow. The target may reject the update because its data model has different constraints to the source. For example, it might not recognise certain email addresses as valid, or it might assume emails are used to identify users and must be unique, which may not be the case in the source. These sorts of validation constraints mean that sometimes, data from the source cannot be represented in the target’s data model.&lt;/p&gt;

&lt;p&gt;If the target system is a remote third-party API, with high latency relative to the system’s own resources, it can also fail for network-related reasons, enforce rate limits, or just add too much latency to apply updates to it inside the scope of normal end-user requests. Therefore the copying of data to the target system is often pushed into a background job queue and is processed asynchronously from where the change originated in the source system.&lt;/p&gt;

&lt;p&gt;This is the final contributor to this ad-hoc approach failing to produce consistency. If changes in the source data are processed asynchronously and may fail and be retried, they will end up being processed in a different order to how they happened. In general, arbitrary updates to data do not &lt;em&gt;commute&lt;/em&gt;, that is, you cannot reorder them without changing their effects. If somebody updates a record and then later deletes it, the replicator will become confused if it tries to delete the record from the target and then update it.&lt;/p&gt;

&lt;h3&gt;
  
  
  ...and Edge-Cases
&lt;/h3&gt;

&lt;p&gt;One additional edge case that emerges in ad-hoc replication is that while the event hooks deal with changes made to the data since their introduction, they do not deal with all the legacy data from before that time. This necessitates a completely different approach to copy over all the existing data, and then keep the target up to date using event listeners. Deciding how to switch from the former to the latter is very difficult because the source data is changing all the time, so it’s hard to execute this correctly without downtime to stop anyone changing the source during the switch-over. Having a whole different code path for a one-off event makes it likely that code path will have defects that go undetected and require future ad-hoc fix-ups to the target data.&lt;/p&gt;

&lt;p&gt;This approach is sometimes likened to &lt;a href="https://en.wikipedia.org/wiki/Change_data_capture" rel="noopener noreferrer"&gt;change data capture&lt;/a&gt; (CDC) or &lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html" rel="noopener noreferrer"&gt;event sourcing&lt;/a&gt;, but it is strictly weaker than both of them. CDC requires a reliable way of identifying which changes have not yet been replicated, and in event sourcing the primary way of recording updates is to write them to a global consistently ordered log, much like a database’s &lt;a href="https://en.wikipedia.org/wiki/Write-ahead_logging" rel="noopener noreferrer"&gt;write-ahead log&lt;/a&gt; (WAL), which is then consumed to produce various representations of the system state.&lt;/p&gt;

&lt;p&gt;Ad-hoc replication is characterised by reactions to changes that can be ignored, fabricated, misinterpreted, reordered, dropped, or executed multiple times, with no reliable way to check and ensure that the target is in sync with the source. It inevitably produces inconsistent states that are hard to debug, not least because there is no formal definition of what state the target should be in, given some state of the source. &lt;strong&gt;There is never one root cause to why the target is in a bad state&lt;/strong&gt;; the reality is that this whole approach is almost guaranteed to produce inconsistency, and needs to be replaced with something else. So what does that something else look like?&lt;/p&gt;

&lt;h2&gt;
  
  
  Diff and Patch
&lt;/h2&gt;

&lt;p&gt;Broadly speaking, replication systems can be classified into two camps: state-based, and operation-based. State-based systems work by examining the current state of the source and target, and making changes to remove any differences between them. Operation-based systems rely on distributing a consistent sequence of events, or a log, to all the systems and having them build their local state off this log. This is how event sourcing works, and how consensus algorithms like &lt;a href="https://raft.github.io/" rel="noopener noreferrer"&gt;Raft&lt;/a&gt; work.&lt;/p&gt;

&lt;p&gt;Many real-world systems use some combination of these approaches, but here I’ll be emphasizing a state-based approach, with some optimisations enabled by event logs. State-based replication tends to be more applicable to disparate systems that only expose their current state to clients and don’t have any way of sharing their event histories such that you could use them as a log.&lt;/p&gt;

&lt;p&gt;The first ingredient of a &lt;em&gt;replication system that actually works&lt;/em&gt;™ is something I hinted at in the previous section. There must be some well-defined way of determining whether a record in the target is consistent with its corresponding source record. This includes the possibility that the target record should not exist, because it is absent or deleted from the source. If it is not in a consistent state, then a description of how it differs from the intended state must be produced, i.e. which operations would be needed to bring the target record into line with the source.&lt;/p&gt;

&lt;p&gt;It is possible this sounds kind of “obvious”; how can you say the target is in an incorrect state if you cannot define what the correct state looks like? However, I have personally seen too many systems where the team did not have a satisfactory answer to this question, and solving this was a prerequisite to making any further progress on the problem.&lt;/p&gt;

&lt;p&gt;You can think of this operation as a sort of “diff and patch” function. The diff tells you how the source and target are out of sync, and is empty if they are in sync. The patch tells you how to change the target to make the diff become empty. For example, imagine we have two systems that contain data on accounts in a social network, and each of them have a certain account in the following state:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source                      Target
------                      ------

{                           {
  "username": "alice",        "username": "alice",
  "location": "london",       "location": "berlin",
  "following": [              "following": [
    "bob",                      "bob",
    "carol",                    "dave",
    "dave"                      "eve"
  ]                           ]
}                           }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;diff&lt;/em&gt; between the target and the source would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;location&lt;/code&gt; field changes from &lt;code&gt;berlin&lt;/code&gt; to &lt;code&gt;london&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;carol&lt;/code&gt; is added to the &lt;code&gt;following&lt;/code&gt; set&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eve&lt;/code&gt; is removed from the &lt;code&gt;following&lt;/code&gt; set&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;em&gt;patch&lt;/em&gt; that removes these differences would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set the target's &lt;code&gt;location&lt;/code&gt; field to &lt;code&gt;london&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;carol&lt;/code&gt; to the target's &lt;code&gt;following&lt;/code&gt; set&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;eve&lt;/code&gt; from the target's &lt;code&gt;following&lt;/code&gt; set&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact details of the diff and patch will depend on what data representation and interface each system actually provides; if the target is a document store, the patch might reduce to a single write containing the whole new state, but if it’s a SQL database it will be possible to update specific columns and add/remove specific rows as required. It is usually desirable to express the diff and patch in the smallest number of operations against each system to maximise performance.&lt;/p&gt;

&lt;p&gt;This sync function must be &lt;em&gt;idempotent&lt;/em&gt; i.e. it should be safe to run any number of times against record in the source system to bring its corresponding target record up to date. This immediately fixes several problems with ad-hoc replication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Replicating a change always works by comparing the full state of the source and target records, rather than assuming the target matched the previous source state and copying some random changes over. This makes sure your event listeners always leave the target in the correct state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It’s not affected by event handlers being reordered by asynchronous processing, retries, and so on. The diff/patch function always does the same thing whenever it is run: it does whatever is needed to match the target match the source.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If it fails due to a transient network error, a rate limit, etc, it can be retried indefinitely on an arbitrary schedule and will eventually succeed and produce the right state, as long as it inspects the state of the source record &lt;em&gt;when it runs&lt;/em&gt; rather than retaining the state from when it was first scheduled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The same code path can be used for copying over all legacy data, and for reacting to future events. You either run the diff/patch function against all existing records, or against a record you know just changed. Eventually this will result in all source records being synced.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a well-defined notion of what it means for a target record to be in an incorrect state, and furthermore, the system itself can identify such states and correct them automatically. If the detection is found to be incorrect, it can be patched and re-run over existing records to resynchronise them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Methods for Comparing
&lt;/h2&gt;

&lt;p&gt;Notice that we now have the same code path being used to handle legacy data, new changes, and retrying in case of failures. This is an example of &lt;a href="https://en.wikipedia.org/wiki/Crash-only_software" rel="noopener noreferrer"&gt;crash-only design&lt;/a&gt;, where you have one code path that handles “normal” operation and failure states. The function’s starting assumption is that the target is in a bad state that needs to be identified and corrected, rather than assuming some good state and invoking an exceptional code path if an error happens. Using the same code path for all eventualities gives you a simpler system that is much more robust.&lt;/p&gt;

&lt;p&gt;This is the core building block of several replication programs. Git’s &lt;code&gt;push&lt;/code&gt; command works by comparing each local branch with its corresponding remote branch, figuring out which commits the remote branch is missing, and copying them over. Rsync works by comparing the file size and modification time of each source and target file (or their checksums if so instructed), and copying the source data to the target if those differ. &lt;a href="https://docs.couchdb.org/en/stable/replication/protocol.html" rel="noopener noreferrer"&gt;CouchDB replication&lt;/a&gt; works by comparing which revisions the source and target have for each doc, and copying over any that the target is missing. In each system, the pattern is the same: compare the source and target records, and transfer whatever data is needed to make them equal.&lt;/p&gt;

&lt;p&gt;A further desirable property of the diff function is that it is fast. This is not quite as important as making it fast to determine which records out of your whole data set are out of sync, which we will cover below, but is still beneficial especially in cases that require a full scan of the source data set.&lt;/p&gt;

&lt;p&gt;For example, Rsync compares files on their size and modification time, which are small bits of metadata that are cheap to read, instead of reading the content of each file, which would be much slower. This assumes that if two files have the same size and modification time, then they very probably contain the same content. This is especially likely given that Rsync will set the mtime of the target to match the source, after it copies the data over and verifies both files have the same checksum. After doing this, it is relatively safe to use the mtime as a proxy for the fact that the data was checked last time Rsync itself updated the target.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimising Compare Behaviour
&lt;/h3&gt;

&lt;p&gt;In Git, comparing two branches is cheap because the way the commit history is stored makes it inherently efficient to identify which commits exist in one branch and not in another. It doesn’t have to employ a “short cut”, it has a data model that directly makes its desired operations cheap. CouchDB has a similar internal structure that makes it cheap to compare the revs of a document between the source and target, so it can minimise which revs need to be copied over without comparing the content of all the revs and sending all that content over the network.&lt;/p&gt;

&lt;p&gt;When designing your own diff functions, it is worth looking for places where you might be able to &lt;strong&gt;skip comparing something expensive by taking short-cuts or using better data structures&lt;/strong&gt;. However, if multiple systems and/or people have the ability to write to the target system, this isn’t always possible and your diff function will need to be “paranoid” and assume the target could be in any random state unless you’ve proven otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell Me the News
&lt;/h2&gt;

&lt;p&gt;Just having a reliable idempotent sync function has already solved a lot of our replication problems. In principle, you could run this function whenever you want against all source records, and this would give you eventual consistency in the target system. However, most source records don’t change most of the time, so scanning through all of them will typically be very wasteful and become slow to react to changes as they happen. To make a replication system practical, we need some way of identifying which records have changed recently. I’ll briefly discuss a couple of common ways of achieving this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Record Flagging Method
&lt;/h3&gt;

&lt;p&gt;The first approach is to put a marker/flag on each record when it is changed, and to remove the marker after the diff/patch function has re-synchronised the record to the target. This is relatively simple to implement using a counter; when the record is changed, the counter is incremented, and when it is re-synchronised, the counter is reset to zero &lt;em&gt;unless it was incremented during sync&lt;/em&gt;. This makes sure that changes made while the record is being synchronised are not ignored, and instead initiate another execution of the sync function. Conditionally resetting the counter can be implemented using &lt;a href="https://en.wikipedia.org/wiki/Compare-and-swap" rel="noopener noreferrer"&gt;compare-and-swap&lt;/a&gt; (CAS) to avoid having to pre-emptively lock the source record and prevent end users interacting with it while it’s being synced to another system.&lt;/p&gt;

&lt;p&gt;How the sync function is executed is completely up to you — after all, it is idempotent and therefore safe to run at any time. If a lot of changes are likely to happen to a record over a short span of time, you might choose to schedule the sync function to run once, a short time interval after the first change is detected, so it picks up all the changes and doesn’t need to be executed for each individual change. If the target benefits from sending changes to it in batches, you can use a cron job to find all the records marked as changed and sync them all at once. Or, if you need less replication latency, you can execute the sync function immediately on each change.&lt;/p&gt;

&lt;p&gt;However, keep in mind that you should only allow one instance of the sync process to run on a given source record at a time; if multiple processes are trying to sync the same record at the same time they may end up getting confusing diffs and producing an inconsistent state in the target.&lt;/p&gt;

&lt;p&gt;To deal with the transfer of legacy data, or to deal with changes and bug fixes in the sync function itself, you can mark all existing records to execute sync against all of them. You will probably want a background process to handle syncing records in batches, just so you can control the throughput and amount of load this generates on the source and target systems, rather than trying to re-synchronise all your data immediately. The outstanding backlog can be easily monitored by counting the number of source records marked for sync.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Event Log Method
&lt;/h3&gt;

&lt;p&gt;The second common method for signalling which records need syncing is to have the application add events to a log whenever a record is changed, and have another process consume this log and use it to execute the sync function against the appropriate records. Whereas the previous approach is a form of change data capture, this technique corresponds to event sourcing. It is typically implemented using background job queues, a database write-ahead log, a message broker like &lt;a href="https://www.rabbitmq.com/" rel="noopener noreferrer"&gt;RabbitMQ&lt;/a&gt; or an event streaming system like &lt;a href="https://kafka.apache.org/" rel="noopener noreferrer"&gt;Kafka&lt;/a&gt;. However you implement it, the core idea is the same: write to the log when a record is changed, and consume from the log to invoke the sync function.&lt;/p&gt;

&lt;p&gt;Note that for this to work, it is critically important that the log is &lt;em&gt;complete&lt;/em&gt; i.e. it faithfully reports all changes to the source data. &lt;strong&gt;The log should not discard an event until the sync function reports that it has been successfully handled&lt;/strong&gt;, i.e. that the target is up to date as of that event. Is it not necessary for the log to preserve event order, indeed in many systems it is not possible to construct a total global order in which source data changes happened. The log is just there to provide a signal about which records need to be synced, so that you do not have to scan your entire data set to find this out.&lt;/p&gt;

&lt;h3&gt;
  
  
  …and How to Choose Which
&lt;/h3&gt;

&lt;p&gt;Whether using record markers or an event log, you must ensure that a change to a record is only considered to have been processed after the sync function reports that it has been successfully handled. Since the sync function is idempotent, this means your change notification does not need &lt;a href="https://blog.bytebytego.com/p/at-most-once-at-least-once-exactly" rel="noopener noreferrer"&gt;exactly-once delivery&lt;/a&gt;, it is fine for it to have at-least-once delivery. In the worst case, the sync function runs multiple times for the same event, determines the target is already in sync, and has no further work to do. If the system has at-most-once delivery, or the sync function fails to retry on transient failures, it will end up not conveying all changes to the target, and the source and target will drift out of sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can CouchDB help?
&lt;/h2&gt;

&lt;p&gt;As a distributed database, CouchDB is built from the ground up to make replication reliable and efficient. Not only is replicating databases between CouchDB instances as simple as a single API call, it gives you the building blocks necessary to replicate from CouchDB into any other system you like with relatively little complexity. In a future article we’ll look in detail at how to use the CouchDB &lt;code&gt;_changes&lt;/code&gt; feed to reliably replicate your CouchDB data to other places.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Discover more ways to keep your CouchDB project efficient on the &lt;a href="https://neighbourhood.ie/blog?pk_campaign=dev&amp;amp;pk_kwd=sync-intro" rel="noopener noreferrer"&gt;Neighbourhoodie blog&lt;/a&gt;, where we regularly publish tips like this, plus guides and tutorials.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>cloud</category>
      <category>architecture</category>
      <category>couchdb</category>
    </item>
    <item>
      <title>Everything You Need to Know About CouchDB Database Names</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 29 Oct 2025 08:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/everything-you-need-to-know-about-couchdb-database-names-1g9g</link>
      <guid>https://dev.to/neighbourhoodie/everything-you-need-to-know-about-couchdb-database-names-1g9g</guid>
      <description>&lt;p&gt;Naming a database might not sound like an exciting activity. But it can be, if you know all the considerations that go into naming a database in CouchDB. Let’s start with the restrictions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restrictions
&lt;/h2&gt;

&lt;p&gt;CouchDB database names have restrictions in terms of which characters can be used. Based on these restrictions, a database name:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;must begin with a lowercase letter from &lt;code&gt;a&lt;/code&gt; to &lt;code&gt;z&lt;/code&gt;, no diacritics etc.&lt;/li&gt;
&lt;li&gt;Each character in the name must be one of:

&lt;ol&gt;
&lt;li&gt;a lowercase letter from &lt;code&gt;a&lt;/code&gt; to &lt;code&gt;z&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a number from &lt;code&gt;0-9&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;an underscore, dollar sign, open or closed parenthesis&lt;/li&gt;
&lt;li&gt;the plus and minus signs&lt;/li&gt;
&lt;li&gt;a slash&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;May be no longer than 238 characters.&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Or expressed as a Regular Expression: &lt;code&gt;^[a-z][a-z0-9_$()+/-]{238}$&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The collection of special characters might seem unfamiliar at first. We’ll explain how they come together further down.&lt;/p&gt;

&lt;p&gt;First, we talk about one of them, the slash, or &lt;code&gt;/&lt;/code&gt;. It is used in URLs and in UNIX-like file systems to denote hierarchy, like a subdirectory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database &amp;amp; File System Limits
&lt;/h2&gt;

&lt;p&gt;In CouchDB 1.x, a database was represented by a single file in the file system. If you had a database called people, CouchDB would store all associated data in a file called &lt;code&gt;people.couch&lt;/code&gt;. And all &lt;code&gt;.couch&lt;/code&gt; database files are stored in the same directory on your file system (it can be found in the CouchDB configuration under &lt;code&gt;[couchdb] database_dir&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;CouchDB does not put a practical limit on how many databases there can be on a server (other than the theoretical ~43&lt;sup&gt;238&lt;/sup&gt;), but file systems do. While those limits are getting higher and higher, in the times of CouchDB 1.x, you had to consider how many databases you would create to get good performance out of your file systems. Some file systems get really slow when there are more than 2&lt;sup&gt;16&lt;/sup&gt; or 2&lt;sup&gt;32&lt;/sup&gt; files in a single directory.&lt;/p&gt;

&lt;p&gt;To make sure you can create more databases than a file system limits you to, CouchDB allows you to add slashes to database names. It will create actual subdirectories in the file system, so you can avoid having too many files in a single directory.&lt;/p&gt;

&lt;p&gt;For example, a database called &lt;code&gt;user/32/14/55187&lt;/code&gt; will be stored in the &lt;code&gt;datatabase_dir&lt;/code&gt; as &lt;code&gt;user/32/14/55187.couch&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Splitting Databases with Sharding
&lt;/h2&gt;

&lt;p&gt;CouchDB 2.0 introduced database sharding, the splitting up of single databases into multiple .couch files, which are stored each in their own directory per shard range. A shard range is expressed as a subdirectory which is named after the range, which goes from &lt;code&gt;00000000&lt;/code&gt; to &lt;code&gt;ffffffff&lt;/code&gt;. For example, a database with four shards (&lt;code&gt;q=4&lt;/code&gt;) occupies the following shard ranges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;00000000-3fffffff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;40000000-7fffffff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;80000000-1bffffff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1c000000-ffffffff&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A database with just one shard occupies the full range:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;00000000-ffffffff&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For databases with a single shard, which are common in the database-per-user pattern, all database files are stored in the same directory on the file system, and the same rules as with CouchDB 1.x apply.&lt;/p&gt;

&lt;p&gt;But the more shards you have per database, the fewer actual files there are in each shard subdirectory in the file system. So you’ll be reaching at which point the file system introduces slowness at a later point, but it is still worth considering if you have a very large number of databases.&lt;/p&gt;

&lt;p&gt;Incidentally, the shard ranges explain why the database name is limited to 238 characters. In the past, file system paths could be at most 255 (2&lt;sup&gt;8&lt;/sup&gt;) characters long. But since CouchDB 2.0 and onwards always includes the shard range, we have to subtract 17 characters (2x8 for the beginning and end of the shard range plus 1 for the dash in the middle).&lt;/p&gt;

&lt;h2&gt;
  
  
  More On Special Characters
&lt;/h2&gt;

&lt;p&gt;And where do the other special characters come in? It is pretty simple actually. When deciding which characters should be allowed in database names, the CouchDB developers surveyed all common file systems and collected all their respective restrictions about what characters could be included in file names. The result is the list of characters allowed in a CouchDB database: all these characters are allowed as part of a file name on any modern file system.&lt;/p&gt;

&lt;p&gt;That said, we usually recommend keeping it to &lt;code&gt;[a-z][a-z0-9-_/]&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Discover more ways to keep your CouchDB project efficient on the &lt;a href="https://neighbourhood.ie/blog?pk_campaign=dev&amp;amp;pk_kwd=db-names" rel="noopener noreferrer"&gt;Neighbourhoodie blog&lt;/a&gt;, where we regularly publish tips like this, plus guides and tutorials.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>couchdb</category>
      <category>database</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>First Steps: Sharding in CouchDB</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 22 Oct 2025 07:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/first-steps-sharding-in-couchdb-g57</link>
      <guid>https://dev.to/neighbourhoodie/first-steps-sharding-in-couchdb-g57</guid>
      <description>&lt;p&gt;While other databases out there might shard, CouchDB is one of the few that does it automatically and saves you the annoying — &lt;em&gt;read&lt;/em&gt; error-prone — work of setting it up yourself. Being unique in this way, it’s a topic you may not know too well. The arcane-sounding term (especially if it reminds you of the &lt;a href="https://stardewvalleywiki.com/Prismatic_Shard" rel="noopener noreferrer"&gt;prismatic&lt;/a&gt; variety) doesn’t need to conjure confusion or intimidation. In this post, we’re going to take a deeper look at scaling CouchDB with shards: what sharding is, plus why and how to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sharding Is &amp;amp; Why It’s Useful
&lt;/h2&gt;

&lt;p&gt;Any given central processing unit (CPU) — the thing on which your database lives — has a physical limit to how much processing it can do at one time. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6adqf3l8hzu4vw29t7j5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6adqf3l8hzu4vw29t7j5.webp" alt="A diagram of a CPU running a GET /test request shows the client sending the HTTP request to the file system via CouchDB to open the .couch shard files" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By extension, there’s a limit to how big your database can get if you still want to do useful things with it if it can only run on a single core.&lt;/p&gt;

&lt;p&gt;Databases each provide their own method to scale with your data and request load as they increase, enabling higher and higher volumes of traffic, more and more reads and writes. Broadly speaking, databases handle this load in one of two ways: scaling vertically or horizontally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vertical Scaling
&lt;/h3&gt;

&lt;p&gt;This usually means throwing more resources at the machine running your database. If you rely on cloud computing, that might mean going up an &lt;a href="https://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud" rel="noopener noreferrer"&gt;EC2&lt;/a&gt; machine size, or if you are running your own machines, migrating your database to a new server with more CPUs or CPU cores. Faster network connection on the new server will also help. However you choose to do it, the goal is to increase processing capacity provided by hardware and/or infrastructure.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;major pro&lt;/strong&gt; of this approach is that by staying focused on the hardware level, you don’t have to deal with the complexities of distributed databases. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;cons&lt;/strong&gt; are that each step you take up in computing power comes with a price tag, and you’re limited in terms of how far you can scale up by how big the biggest available machine at the time is. &lt;/p&gt;

&lt;h3&gt;
  
  
  Horizontal Scaling
&lt;/h3&gt;

&lt;p&gt;The other way you can take things mainly means splitting the data you have into &lt;a href="https://en.wikipedia.org/wiki/Logical_partition" rel="noopener noreferrer"&gt;logical partitions&lt;/a&gt;. These parts can then be distributed across multiple CPU cores and even across multiple computing nodes. By doing so, you can change the way data is processed to make more efficient use of existing resources rather than having to buy ever higher-performance servers. If you opt for a horizontal scaling strategy, then adding hardware resources looks like adding more of the same kind of servers onto which data are distributed.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;biggest pro&lt;/strong&gt; here is that you can scale beyond the capacity of a single machine. Also, combining multiple smaller machines is cheaper than splurging on the one really powerful machine you’d need to scale vertically. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;usual con&lt;/strong&gt; in a horizontal scaling scenario is that distributed databases add a lot of complexity. I say &lt;em&gt;usually&lt;/em&gt; because CouchDB’s clustering features make a lot of this go away, and instead your cluster looks like a single, but powerful and redundant server.&lt;/p&gt;

&lt;p&gt;CouchDB is &lt;a href="https://neighbourhood.ie/blog/2025/02/05/couchdb-is-great-for-prototypes-and-side-projects" rel="noopener noreferrer"&gt;especially good&lt;/a&gt; at horizontal scaling. One of the ways it works around physical limitations is by using &lt;em&gt;shards&lt;/em&gt;, the logical parts it can create and distribute. Shards also mean &lt;strong&gt;CouchDB has the performance benefit of shorter access paths&lt;/strong&gt;. If you logically split shards along document ids, for example, then you can get much faster read and write performance since each shard now has a &lt;code&gt;log(1/number-of-shards)&lt;/code&gt; shorter path to the document itself. This means you can get away with less powerful — &lt;em&gt;read&lt;/em&gt; cheaper — hardware.&lt;/p&gt;

&lt;p&gt;Another benefit CouchDB gains by using shards is that you can even further distribute your required computing across multiple nodes when replicas are all on their own machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Databases That Shard
&lt;/h2&gt;

&lt;p&gt;Let’s approach databases that shard in terms of two categories: &lt;em&gt;relational&lt;/em&gt; and &lt;em&gt;NoSQL&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;In relational databases — as the name suggests — various pieces of data are closely coupled with one another. This makes sharding inherently difficult because there is a lower limit to how data can be de-coupled and split in a schema. Because it’s not what they were strictly designed to do, popular relational databases like Postgres, MySQL and MSSQL rely on third-party sharding extensions. &lt;a href="https://github.com/citusdata/citus" rel="noopener noreferrer"&gt;Citus&lt;/a&gt; for Postgres, for example, enables sharding and uses reference tables replicated to all nodes to get around distributing data relations. One notable exception to this is OracleDB, which might be the only SQL database with native sharding capabilities. &lt;/p&gt;

&lt;p&gt;Now for the NoSQL databases. Noteworthy in this category is MongoDB, which does shard, but not automatically —you have to enable it. Also, confusingly, MongoDB &lt;a href="https://www.mongodb.com/resources/products/fundamentals/clusters" rel="noopener noreferrer"&gt;conflates&lt;/a&gt; sharding and setting up a cluster, which can be a bit confusing.&lt;/p&gt;

&lt;p&gt;So, CouchDB remains somewhat an outsider in that it’s one of the few database systems that shards automatically and &lt;em&gt;transparently&lt;/em&gt;, as in: your application is none the wiser and just benefits from the additional hardware resources as you grow. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sharding in CouchDB
&lt;/h2&gt;

&lt;p&gt;In CouchDB, a &lt;strong&gt;shard&lt;/strong&gt; is a file on disk with a &lt;code&gt;.couch&lt;/code&gt; ending. You can also configure how many replicas your database will make of each shard in a scenario where you have more than one node.&lt;/p&gt;

&lt;p&gt;When it comes to configuring your shards, there are two important values to keep in mind: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One is &lt;code&gt;q&lt;/code&gt;, which is the number of shards per database. In CouchDB, sharding can be controlled &lt;strong&gt;per database&lt;/strong&gt; and doesn’t have to be an overarching instance configuration. You can have one database in four shards, one in two shards and another with one shard, &lt;a href="https://neighbourhood.ie/blog/2020/09/22/sharding-choosing-the-right-q-value" rel="noopener noreferrer"&gt;for example&lt;/a&gt;. The default value is 2, and we’ll cover guidelines for a limit per shard a bit later on. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The other parameter is &lt;code&gt;n&lt;/code&gt;, which is the number of &lt;em&gt;shard replicas&lt;/em&gt;. Shard replicas make sure you have multiple, independent copies of your data distributed &lt;strong&gt;across multiple nodes&lt;/strong&gt; — which is when this parameter becomes interesting — but no node can have more than one replica of the same shard. For a single node, by default &lt;code&gt;n = 1&lt;/code&gt;. Having multiple replicas of the same shard on a single node doesn’t have benefits for data resilience and would just waste disk space.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A database can be stored in 1…n shard files. Each shard is still limited by a single CPU, but 2 shards can be handled by 2 separate CPU cores. 4 shards can be handled by 4 cores, 8 shards by 8 cores, and so on. Very large databases can have a &lt;code&gt;q&lt;/code&gt; of 16, 32, 64 or more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzptstjt544mu5ptbezj0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzptstjt544mu5ptbezj0.webp" alt="In this diagram of a database sharded using consistent hashing, the database is represented by a large rectangle. It contains small squares representing individual documents from A to G with an ellipses indicating continuation. Below this are 4 smaller rectangles, representing a q value of 4. Each smaller rectangle contains two documents, and the row is not alphabetical." width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a cluster, the default number of shard replicas or &lt;code&gt;n&lt;/code&gt; value is 3. So for example if you have &lt;code&gt;q = 2&lt;/code&gt; and &lt;code&gt;n = 3&lt;/code&gt; (which is the default in the cluster) you would have 6 shards in total, per database. &lt;/p&gt;

&lt;p&gt;In CouchDB shard ranges are hashed from the document IDs using the &lt;a href="https://en.wikipedia.org/wiki/Cyclic_redundancy_check" rel="noopener noreferrer"&gt;CRC&lt;/a&gt; 32 hash, which achieves a relatively uniform hash distribution. What this means is that you don’t need to worry about your ID distribution, because CouchDB hashes your document IDs before adding them to a shard. The result is that shards won’t be — or will very rarely be — different in size. Usually shards are fairly evenly balanced (unless you have a lot of attachments or conflicts, but that is not a sharding concern).&lt;/p&gt;

&lt;p&gt;Here's an example showing two shards in a database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Shard 1: Hashed IDs 00000000-7fffffff
Shard 2: Hashed IDs 80000000-ffffffff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One shard has the hexidecimal range &lt;code&gt;00000000-7fffffff&lt;/code&gt;, and the other shard has the range from &lt;code&gt;80000000-ffffffff&lt;/code&gt;. All your document IDs will be hashed to fit into one of these ranges and each ID here has a ~50% chance of landing in either one.&lt;/p&gt;

&lt;p&gt;A major benefit of sharding in CouchDB is &lt;strong&gt;more efficient view indexing&lt;/strong&gt;. By having multiple parts of your data, you can process view indexes in parallel. CouchDB can spawn &lt;code&gt;q couchjs&lt;/code&gt; processes, effectively doubling the throughput for each doubling of &lt;code&gt;q&lt;/code&gt;. This works as long as the respective amount of CPU cores are available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing Stuff With CouchDB Shards
&lt;/h2&gt;

&lt;p&gt;Let's quickly have a look at some cool stuff you can do with shards. The most basic thing you can do is to send an HTTP GET request to your database to check its sharding parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://mynode1:5984/mydbname
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cluster"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"n"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we can see the cluster key which tells us the value of &lt;code&gt;q&lt;/code&gt; and of &lt;code&gt;n&lt;/code&gt;. We can also see values for &lt;code&gt;w&lt;/code&gt; and &lt;code&gt;r&lt;/code&gt;, &lt;a href="https://docs.couchdb.org/en/stable/cluster/sharding.html#quorum" rel="noopener noreferrer"&gt;read and write quorums&lt;/a&gt; which, for the sake of staying on topic, we won’t go further into in this article. &lt;/p&gt;

&lt;p&gt;We can also find out where exactly shard replicas are placed in a cluster by calling the &lt;code&gt;mydbname/_shards&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://mynode1:5984/mydbname/_shards
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shards"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"00000000-7fffffff"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mynode1@localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mynode2@localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mynode3@localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that the first shard, with shard range &lt;code&gt;00000000-7fffffff&lt;/code&gt; is stored on node 1, 2 and 3 — exactly as it should be. In a scenario with four nodes where &lt;code&gt;n = 3&lt;/code&gt;, it might (correctly) be 1,2 and 4.&lt;/p&gt;

&lt;p&gt;One cool thing you can do is split an existing shard into two smaller ones using the &lt;code&gt;_reshard&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl
  &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://mynode1:5984/_reshard/jobs
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "type": "split"
    "db": mydbname",
    "range": "00000000-7fffffff"
    }'&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt;

&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;:true, …&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;span class="c"&gt;#q will now be 3 for this database&lt;/span&gt;
&lt;span class="c"&gt;#Keep in mind that shard ranges are now&lt;/span&gt;
&lt;span class="c"&gt;#unbalanced unless you split the other range too.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this request, we’re asking CouchDB to split the shard with range &lt;code&gt;00000000-7fffffff&lt;/code&gt; on this database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By adding a shard we’re changing the &lt;code&gt;q&lt;/code&gt; value to 3&lt;/li&gt;
&lt;li&gt;And end up with three shards:

&lt;ul&gt;
&lt;li&gt;One untouched shard, in its original size&lt;/li&gt;
&lt;li&gt;Two smaller shards split from one shard, equal in size to each other but half the size of the untouched shard&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;While this is very neat to do, don’t forget that leaving our CouchDB as-is after sending this request and &lt;strong&gt;keeping shards of differing sizes would not be ideal&lt;/strong&gt;, and can lead to degraded performance. We’d want to split our other shard too and have &lt;code&gt;q = 4&lt;/code&gt;, and as a rule of thumb, when &lt;a href="https://neighbourhood.ie/blog/2020/09/29/sharding-increasing-the-number-of-shards" rel="noopener noreferrer"&gt;increasing the number of shards&lt;/a&gt;, always ensure we’re splitting &lt;em&gt;all&lt;/em&gt; shards and keeping them roughly equal in size. &lt;/p&gt;

&lt;p&gt;Reducing your &lt;code&gt;q&lt;/code&gt; value is not possible in CouchDB out-of-the-box: the &lt;code&gt;_reshard&lt;/code&gt; endpoints only support splitting shards up, not down. But if you were hoping to read the contrary we have good news! At Neighbourhoodie we developed a little NodeJS CLI tool called &lt;a href="https://neighbourhood.ie/blog/2020/10/06/sharding-reducing-the-number-of-shards" rel="noopener noreferrer"&gt;couch-continuum that reduces the number of shards&lt;/a&gt; for you. &lt;/p&gt;

&lt;p&gt;The procedure is not too difficult, and you could still &lt;a href="https://docs.couchdb.org/en/stable/cluster/sharding.html" rel="noopener noreferrer"&gt;reduce shards manually&lt;/a&gt; if you wanted to. It basically consists of creating a new, replica database with the new &lt;code&gt;q&lt;/code&gt; value, replicating your data there, and then destroying the original source database, recreating it with the new &lt;code&gt;q&lt;/code&gt; value and replicating the data back. This solution will incur some downtime. You could avoid that by updating your application code to point toward the replica database, but that’s a topic a bit beyond the scope of this article. Suffice it to say it requires some consideration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to Consider in Production
&lt;/h2&gt;

&lt;p&gt;The first thing we recommend is to start with the default &lt;code&gt;q&lt;/code&gt; value of 2 (since CouchDB 3 onwards) and split your shards with the &lt;code&gt;_reshard&lt;/code&gt; endpoint as your database grows. This takes away a lot of the stress of &lt;a href="https://neighbourhood.ie/blog/2020/09/22/sharding-choosing-the-right-q-value" rel="noopener noreferrer"&gt;choosing your &lt;code&gt;q&lt;/code&gt; value&lt;/a&gt; up-front, because as we’ve seen, it’s much easier to go up in &lt;code&gt;q&lt;/code&gt; values than to go down. Providing a larger &lt;code&gt;q&lt;/code&gt; value than you need would mean consuming more resources than you need to, for no clear benefit. CouchDB will tend to consume the resources you give it, and a large &lt;code&gt;q&lt;/code&gt; value will set it on a hungry path. We’ll look at rough guidelines for a limit per shard in the next section.&lt;/p&gt;

&lt;p&gt;Another rule of thumb: &lt;code&gt;n&lt;/code&gt; should always be &lt;strong&gt;at least&lt;/strong&gt; equal to the number of nodes in your cluster, &lt;strong&gt;but not more than 3&lt;/strong&gt;. It’s kind of self-explanatory: you’d want your system to make use of the breadth of infrastructure you’ve made available to it, you can’t just have a full copy of all data on each shard. And also, having three copies means that if one node fails for some reason, you still have redundant access to your data, so while you wait for that node to recover or be replaced, you can still lose one more node and your application is, again, none-the-wiser. It is very unlikely that you need to protect against even more cascading errors beyond that.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a&gt;&lt;/a&gt;Rough Guidelines For a Limit Per Shard
&lt;/h3&gt;

&lt;p&gt;As a very general rule of thumb, and depending on your attachment sizes, we recommend increasing your shard limit or &lt;code&gt;q&lt;/code&gt; value when each shard has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1GB — 10GB active data size per shard, or&lt;/li&gt;
&lt;li&gt;1M — 10M documents, whichever comes first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, if you have 1000s of requests and one of your CPU cores peaks at 100%, you might run into the limit. If you observe this level of usage, it’s sensible to upscale. &lt;/p&gt;

&lt;h3&gt;
  
  
  Good To Know Before Sharding
&lt;/h3&gt;

&lt;p&gt;There are potential downsides to sharding, depending on your use case or frequent processes you might need your system to run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View joins&lt;/li&gt;
&lt;li&gt;Mango &lt;/li&gt;
&lt;li&gt;&lt;code&gt;_all_docs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;and &lt;code&gt;_changes&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;tend to cause more resource usage, as each request needs to talk to &lt;code&gt;q&lt;/code&gt; shards. Partitioning can help by collecting data with a common prefix on a single shard instead of distributing the data evenly across shards. We’ve written about &lt;a href="https://neighbourhood.ie/blog/2025/03/12/understanding-database-partitioning-in-couchdb" rel="noopener noreferrer"&gt;how partitioning works and which use cases it’s suited to&lt;/a&gt;: check it out if your project will be join  or &lt;code&gt;_all_docs&lt;/code&gt;-heavy, for example.&lt;/p&gt;

&lt;h3&gt;
  
  
  Splitting Shards
&lt;/h3&gt;

&lt;p&gt;We mentioned this before, but it’s important that your shards are of a similar size to one another in order to avoid performance quirks. You should always split shards quadratically: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 → 4&lt;/li&gt;
&lt;li&gt;4 → 8&lt;/li&gt;
&lt;li&gt;8 → 16 &lt;/li&gt;
&lt;li&gt;16 → 32&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consider Document Design
&lt;/h3&gt;

&lt;p&gt;Shards help you do some pretty cool stuff with CouchDB! But if you’re not configuring CouchDB correctly, or if you don’t have a sound and reasonably considered approach to document design, sharding won’t help you mitigate the impact of that.&lt;/p&gt;

&lt;p&gt;One of the most important things to think about early in CouchDB is how you’ll achieve a consistent and scalable document model. CouchDB can do a lot of things, but it can’t do everything, so getting your docs in a row ( 🦆!) is your best way to ensure steady performance from the outset. We’ve written up a couple of tips to get you going: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://neighbourhood.ie/blog/2025/02/13/couchdb-data-modelling-prefer-smaller-documents" rel="noopener noreferrer"&gt;CouchDB Data Modelling: Prefer Smaller Documents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neighbourhood.ie/blog/2025/02/19/couchdb-data-modelling-prefer-smaller-attachments" rel="noopener noreferrer"&gt;CouchDB Data Modelling: Prefer Smaller Attachments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Shards serve multiple purposes in CouchDB — they’re essential to its scaling strategy and data availability, and a key part of using CouchDB as a distributed system. &lt;strong&gt;Most importantly&lt;/strong&gt;, you mostly won’t have to think about shards as CouchDB handles them for you transparently. Only when your application becomes a major success will you have to take them into account, and even then they’re pretty straightforward to handle and &lt;strong&gt;your application will not need a rewrite&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We recommend diving more deeply into the topic in the official &lt;a href="https://docs.couchdb.org/en/stable/cluster/sharding.html" rel="noopener noreferrer"&gt;CouchDB Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article owes a huge debt to Olivia Hugger and is based on her presentation &lt;em&gt;&lt;a href="https://www.youtube.com/watch?v=vPc16P6FaZk" rel="noopener noreferrer"&gt;Sharding in CouchDB for Fun &amp;amp; Profit&lt;/a&gt;&lt;/em&gt; which you can watch, in full, on our YouTube channel.&lt;/p&gt;

&lt;p&gt;If you would like insights to help &lt;a href="https://neighbourhood.ie/products-and-services/couchdb-architecture-review" rel="noopener noreferrer"&gt;optimise your architecture&lt;/a&gt;, need a &lt;a href="https://opservatory.app/" rel="noopener noreferrer"&gt;CouchDB monitoring tool&lt;/a&gt;, or don’t yet know what you need — &lt;a href="https://neighbourhood.ie/call" rel="noopener noreferrer"&gt;give us a call&lt;/a&gt;. We are curious and would love to help you and your project be successful.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Discover more ways to keep your CouchDB project efficient on the &lt;a href="https://neighbourhood.ie/blog?pk_campaign=dev&amp;amp;pk_kwd=shard" rel="noopener noreferrer"&gt;Neighbourhoodie blog&lt;/a&gt;, where we regularly publish tips like this, plus guides and tutorials.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>couchdb</category>
      <category>database</category>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Sharding in CouchDB: Reducing the Number of Shards</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Wed, 15 Oct 2025 08:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/sharding-in-couchdb-reducing-the-number-of-shards-2h74</link>
      <guid>https://dev.to/neighbourhoodie/sharding-in-couchdb-reducing-the-number-of-shards-2h74</guid>
      <description>&lt;p&gt;In contrast to increasing the number of shards for a database, reducing the number of shards is not a built-in operation. In addition, as shard splitting is only available in CouchDB 3.x and later, this advice is good for version 2.x as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  couch-continuum
&lt;/h2&gt;

&lt;p&gt;Neighbourhoodie has built the &lt;a href="https://github.com/neighbourhoodie/couch-continuum" rel="noopener noreferrer"&gt;&lt;code&gt;couch-continuum&lt;/code&gt;&lt;/a&gt; tool that automates the bulk changing of database parameters, including the number of shards for a database.&lt;/p&gt;

&lt;p&gt;This tool can both increase and decrease the number of shards for a database in both CouchDB 2.x and 3.x.&lt;/p&gt;

&lt;p&gt;There is just one caveat: it can not operate without taking the original database offline for the duration of its restore. So you can only do this during a maintenance window.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Discover more ways to keep your CouchDB project efficient on the &lt;a href="https://neighbourhood.ie/blog?pk_campaign=dev&amp;amp;pk_kwd=reduce-shards" rel="noopener noreferrer"&gt;Neighbourhoodie blog&lt;/a&gt;, where we regularly publish tips like this, plus guides and tutorials.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>couchdb</category>
      <category>database</category>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Sharding in CouchDB: Increasing the Number of Shards</title>
      <dc:creator>Maddy</dc:creator>
      <pubDate>Thu, 09 Oct 2025 08:00:00 +0000</pubDate>
      <link>https://dev.to/neighbourhoodie/sharding-in-couchdb-increasing-the-number-of-shards-145m</link>
      <guid>https://dev.to/neighbourhoodie/sharding-in-couchdb-increasing-the-number-of-shards-145m</guid>
      <description>&lt;p&gt;This advice is &lt;strong&gt;only true for CouchDB 3.0.0&lt;/strong&gt; or later. &lt;a href="https://neighbourhood.ie/blog/2020/10/06/sharding-reducing-the-number-of-shards/" rel="noopener noreferrer"&gt;Next week&lt;/a&gt;, we’ll cover increasing the number of shards in CouchDB 2.x.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shard Splitting
&lt;/h3&gt;

&lt;p&gt;Increasing the number of shards in CouchDB is implemented by a technique called &lt;em&gt;&lt;a href="https://docs.couchdb.org/en/stable/cluster/sharding.html#splitting-shards" rel="noopener noreferrer"&gt;shard splitting&lt;/a&gt;&lt;/em&gt;. It allows you to specify for any one shard in a database to be split into two equal-sized shards.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Reshard Endpoint
&lt;/h3&gt;

&lt;p&gt;CouchDB does all the hard work for you: a single request to &lt;a href="https://docs.couchdb.org/en/stable/api/server/common.html#reshard" rel="noopener noreferrer"&gt;the /_reshard endpoint&lt;/a&gt; will start the process. Shard splitting is a background process that can go on while your CouchDB cluster is fully operational. Once the split shards are available, CouchDB starts using them to serve requests instead of the original shard, which then gets deleted.&lt;/p&gt;

&lt;p&gt;Note that shard copies are replicated across your cluster. We recommend you split one shard at a time. In addition, we recommend to split &lt;em&gt;all&lt;/em&gt; shards of a database around the same time in order to guarantee consistent performance.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Discover more ways to keep your CouchDB project efficient on the &lt;a href="https://neighbourhood.ie/blog?pk_campaign=dev&amp;amp;pk_kwd=increase-shards" rel="noopener noreferrer"&gt;Neighbourhoodie blog&lt;/a&gt;, where we regularly publish tips like this, plus guides and tutorials.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>couchdb</category>
      <category>database</category>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
  </channel>
</rss>
