Skip to content
This repository has been archived by the owner on Feb 9, 2018. It is now read-only.

Quill v2

Vicky Chijwani edited this page Aug 23, 2017 · 40 revisions

This is a brain-dump of thoughts for the next major version of Quill.

Write tests first

Before doing a big rewrite, write acceptance tests. Read this summary of Michael Feathers' Working Effectively with Legacy Code.

Design

Consider a white+accent minimal theme like Medium and Asana. Current theme doesn't fit well with an app focused on writing. Other inspirations: iA Writer.

Axes of change

What changes will need to be done frequently?

  • Compatibility changes as Ghost evolves (hopefully there will be fewer breaking changes after 1.0)
  • Adding new fields in domain objects and exposing them in the UI
  • Editor refinements
  • Offline operation improvements (do we need to persist state as described here and in other recent "Building for Billions" talks?)
  • Post content check-pointing for undo/time travel
  • UI refinements

Considerations

  • Testability
  • Independence from 3rd-party libs (using adapters?), especially Realm because its model is counter-intuitive which causes a lot of crashes (best option: do a safe integration of Realm, rather than a deep integration)
  • Independence of core business logic from Android-specific APIs (quite difficult to do)
  • Ghost 1.0 changes (especially at the DB level)
  • Complexity of network code (e.g., onSyncPostsEvent code; forceNetworkCall vs loadCachedData; ...)
  • Complexity of post state machine
  • Background sync
  • Figuring out what has changed on the server since the last time we checked - this is not as easy as it seems
  • Multiple blogs logged in simultaneously
  • Fully-offline operation (including image upload?)
  • Image upload decoupled from editor UI (e.g., when sharing photos from other apps)

Known issues in current design

  • Edit draft on phone, lock screen, publish from Ghost, unlock screen => published post is overwritten! (issue link)
  • Spurious conflicts detected, aggravated by naive sync timing (see Fabric for stats)
  • Temporal dependency of network requests is unoptimized, e.g., user info must be loaded before loading posts (to enforce Ghost's role-based permissions), and post upload must happen before download - there should be a declarative way to specify these dependencies, instead of the naive "refresh events queue" that we have now
  • When a post is changed in the background while it is being edited, the logic to handle it is very flaky (PostReplacedEvent etc). Relevant issues: #125
  • Can't cancel ongoing requests (e.g., stuck image uploads). With large timeouts (5 mins for WRITE_TIMEOUT), the ability to cancel - exposed to the user - is even more critical.
  • See FIXME comments in code
  • Keep other known issues in mind, see the labelled issues

Persistence architecture

Validate this against Ghost 1.0 OAuth plans (iirc they're planning to have a single auth mechanism for all blogs related to a single person - "person" might mean a Ghost account, or something else entirely).

  • Provide a BlogDataStore interface to query and update a blog/account's data, have 2 implementations (NetworkBlogDataStore and CacheBlogDataStore perhaps, since we use a write-back cache strategy mostly).
  • For the CacheBlogDataStore:
    • ✓ Store data for each blog in a separate Realm file
    • ✓ Move all blog url, credentials, etc (everything in UserPrefs currently) to the default Realm
      • ✓ Additionally map blog url + username => Realm file for that blog (store this mapping in the default Realm itself)

Dependency upgrades

Ideas

  • Architecture inspiration (use wisely!):
  • Represent the post state machine in its own class, independent of Android/Realm stuff for testability
    • Use a lightweight state machine library for structure
  • Functional core, imperative shell (https://www.destroyallsoftware.com/talks/boundaries)
    • Make domain objects like Post immutable
    • If mutability is necessary, try to draw on ideas from React and the concept of Monads
  • Make all network calls synchronous:
  • Inject dependencies as far as possible, avoid singletons except for logging/analytics
  • Use Jake Wharton's "Robot pattern" for UI tests
  • Maybe use RxJava to refactor the ugly refresh and post upload logic (get rid of the queues!)
  • Must store several copies of each post, at least these:
    1. Current edited, unsynced copy
    2. Copy of the post as it was when last synced from the server (in order to support "discard unpublished edits" offline; to show diff before publishing edits; and for better conflict-detection and possibly resolution, i.e., show the diff relative to common ancestor and the last-uploaded time of the device copy, maybe try a 3-way merge like git does - this last is probably overkill)