This repository has been archived by the owner. 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)
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.