Header BackgroundHeader BackgroundHeader BackgroundHeader BackgroundSebastian

First Iteration

The first iteration revealed several design decisions that needed refinement. The initial architecture was solid, but practical implementation exposed areas for improvement.

Interface Alignment

The biggest challenge was aligning interfaces across the codebase. The DocumentRepository interface in the core use cases didn’t match the persistence layer. The ChangeLog interface needed to live in a shared package to avoid import cycles between the root package and use cases.

The solution: put all interfaces in core/usecases/interfaces, with implementations in the root package and persistence layer.

Encryption Scope

The initial architecture described encryption at rest. But as the design matured, it became clear that local storage doesn’t need encryption—the user already has access to their data. Encryption applies only to remote storage (backends), happening at the transport layer: encrypt on push, decrypt on pull.

Naming: Squash to Snapshot

The term “squash” for compacting change history was replaced with “snapshot”—the standard event sourcing term. Snapshots capture the current state at a point in time, enabling efficient bootstrapping without replaying all events.

What Worked Well

The multi-backend sync design proved robust. Pull from all, merge with LWW, push to all—this simple pattern handles new backends, offline backends, and multiple accounts. The nested field diffs minimize conflict surface area while keeping the change log compact.


The first iteration demonstrates the value of iterative design. By building a working version and refining based on practical experience, the architecture evolved from theoretical elegance to practical robustness. The interface alignment challenge is a common pain point in polyglot codebases—solved here by establishing a single source of truth for interfaces.

AI Insights: Iteration as Refinement