Initial Stack for the Noteland Web App

My initial values and principles for developing Noteland helps to narrow down some guidelines for choosing a stack for the web app:

  • It should be easy to maintain because I’m not working on it full time. I need to be able to jump in quickly, iterate, and fix bugs. Resolving bugs and adding new features should happen ‘refreshingly fast’.
  • Visual feedback should be fast and support a quick iterative loop since it’s still experimental and will need frequent, small changes along the way. ‘Refreshingly fast’ extends to my own environment because speed is undervalued.
  • The app should be very fast for users (e.g. initial download size, time to render, snappy interface), page reloads should feel nearly as fast as a single page application loading in-place, and Google Lighthouse measurements of website performance should be 100 for desktop and mobile.
  • Rely on the web stack and have good support for a langua franca like markdown.

To try out different stacks, I’m building a prototype note editor that displays a full screen text editor with minimal syntax highlighting for GitHub flavored markdown and dev server that can do live reload.

Frontend

Stack 1: TypeScript and Webpack

My experience thus far has not been good. In a tweet, I mentioned the difficulty getting a simple hello world web app with typescript and css set up with webpack. In the end it worked, I can compile and live reload the page, but it takes over 8 seconds for a program with 10 lines of code (excluding imports). Webpack suffers from aiming to be so powerful and generic it is a platform which does just about nothing. From my beginner eyes, Webpack merely organizes a myriad plugins and extensions to execute in an unknown sequence–which feels like a hulking mess with bad performance on page load and development feedback loop.

This commit implements the prototype in Typescript and Webpack.

Update: I was able to get the reload speed down to 3 seconds and the production bundle down to ~300 kb by setting the development and production flags in Webpack config. There’s seems to be more options for tree shaking, so it’s an acceptable page weight for now.

Stack 2: Vanilla js

What if we could ditch all the machinery used by modern js? I could drastically reduce the amount of js code that would need to be shipped to the client by using contenteditable (introduced in HTML5) which forms a psuedo text editor that is supported directly by most browsers. There’s some notable quirks between browser implementations, but it would support syntax highlighting too (or even wysiwyg rendering). Live reloading is fast and easy because there is nothing to compile.

Stack 3: Vanilla js + wasm

Markdown rendering in webassembly is faster than JavaScript and nearly the same weight as a comparable js dependency. Using the pre tag with contenteditable creates a live rendering while typing similar to Dropbox Paper. It might even be good enough to skip a build step server side when publishing a new note.

Backend

S3

In some ways S3 is ideal—the frontend can make requests directly securely using AWS Cognito, no setup to start storing documents, and plays nicely with AWS Lambda for doing things like generating the published version of notes.

On the other hand, reading data out is too constrained. Implementing a list of notes, for example, would require making a call to list keys and then fetching each object. Because keys are stored in chronological order that could require multiple calls to list keys to get a recent time range. All together, this would introduce a large amount of latency without additional caching or storing data on the client. It might be possible to keep an index file with the keys and titles up-to-date manually.

S3 with client-side offline storage

localstorage has a limit of 5MB per domain, which won’t be enough for large note sets. IndexedDB can store considerably more, ~500MB per domain. There is a new Storage API that attempts to abstract over the various local storage backends, but it’s not fully supported by all browsers (notably Microsoft Edge).

Storing more locally has the added advantage of being fast and the ability to work offline (or install as a PWA).

S3 + RDS as an index

You can set up a Lambda hook to update an index stored in a database. Then you could query the DB when you need an index. Based on the pricing model (writes, reads, storage), it should be cheap to run just an indexing service. However, if we’re going to have a database, you could simplify everything by just storing all the notes in a database too and cut out the middle man (S3).

S3 + Algolia

Eventually full text search will be needed of both note titles and contents. An event would be triggered whenever an S3 object is created/updated/deleted so we could index the document in Algolia and make it available for search. There is a free tier that should be good enough for an MVP.

AWS Aurora

It seems likely that a full database will be needed eventually, so what if we jumped right to it? We could still keep it serverless (low cost) by using something like AWS Aurora which can auto-scale and turn on only when needed. However, there is a 5-30 second cold start time depending on what blog post you read. The recommendations around it are to… keep a single instance running at all times which is expensive and defeats the purpose.

There’s a lot of other neat tools which might be useful in the future like AWS App Sync (graphql, offline sync). For now though, it adds too much complexity for such a simple app.

Postgres

Similar to the argument above, if we can’t actually get away without having a DB, and Aurora isn’t acceptable serverless due to cold starts, we could go back to ol' reliable Postgres running a full time instance on AWS RDS.

The downside is that this ends up looking like a more typical centralized CRUD app which goes against the ‘last forever’ part of the principles of Noteland since data is no longer files that have a 1:1 relationship with what shows up when publishing and the user wouldn’t have any other options for how they store the data.

Git

This is an intriguing option because the user would more or less own the storage layer and Noteland would operate as a client and publisher. Git can inter-operate on many platforms and open up many different workflows (such as my current note taking workflow). If a user doesn’t want to add their own git repo, one could be created for them and operate transparently.

  • Javascript Text Editors for Markdown

    Below is a list of JavasScript libraries that support markdown. Static content can be syntax highlighted using libraries with a much smaller footprint, but editing text with syntax highlighting takes significantly more code. I’ve attempted to make syntax highlighters into a simple editor, but quickly ran into issues with pre elements, cursor positioning, and cross-browser quirks of contenteditable.

  • § What I Learned 2020

    COVID-19