Drafts and Design

Tonight was the first night in a while that I really got to sit down and hack on this website. I'm glad it was, because I had a fully-formed idea and design for a new backend feature which I just cranked out, as well as some fixes to the backend so here goes.

As a writer (a title for which I barely qualify) there comes a time when you look upon what you have written and think "this is utter trash" and "damnit I wish I had changed or reconsidered that paragraph before publishing". Because of this simple fact of writing, I have long wished for a draft feature in this blog. Just as a filter between my mind and the data immortality of the internet you understand.

This is, I hope, the last post in which this is really true but the life cycle of a post on my blog for now is approximately

  1. hack a new feature
  2. test it
  3. fix apparent bugs, goto 2.
  4. commit, deploy it
  5. it's now 3AM. I'm exhausted. Write a quick blurb and hit the sack.

Anyway. Onto the (vaguely) technical bit.

The First Cut

I confess that I took the naive approach to a draft system. In order to implement drafts, I just added a boolean flag to my standard MongoDB object format which represents whether or not the post is ready for general consumption. The routine which generates the front page checks the database for this flag and will display a post on the front page only if it is unset (being the ready for the world at large).

I have a new button in the administration console which let me say "save as draft" for both editing existing posts and for new posts.

Issues With The Implementation

As I was typing this post I realized that under my draft scheme it is quite possible for me to conceal an old post by flagging it as a draft, and that when I un-flagged it from being a draft the page although unmodified would bear an updated timestamp and show that the post was created whenever I flagged it "not a draft". This set off the alarm bells that I had created a Kludge which while perhaps functional was at hear insufficient for the task at hand.

The Second Cut

The more general solution is that I change my post format specification again, this time to include a nested JSON object which represents access rights to the post kinda like Unix systems have file permissions. I can then limit queries (like those used to generate the front page) to those posts which match a set of read permissions that designate the general public. I can have my own or even several read-permission code(s) which are used for drafts and such, and I can even have read permissions for users (when I get around to implementing those) and even for various classes of user.

The issue with this approach is that while effective, it doesn't truly solve the issue at hand. It is indeed a more general and useful access control mechanism than what amounts to a single embedded bit-field. While I am glad to have had this idea and I'll throw it on the implementation queue, the issue of drafts isn't well solved yet. Sure I can now control access to drafts more effectively but the timestamp and post-hiding issues suggest that I need a way to manage and separate drafts from posts.

The Final Solution

Down this road lies madness. The ideal draft system would in fact be an embedded form of Git which would feature branches and merging of posts. a Git-Flow like strategy of keeping drafts in a development branch and only pushing to master when ready to publish would then serve well. However I have neither time nor patience to create a clone of git within a side project, so I need a sub-optimal solution.

What Makes Sense To Do

The solution which makes the most sense is to maintain a table of drafts which is separate from my table of visible (or hidden) posts. Each draft has the full text, drafts are grouped and labeled for what post they are sourced from and a publish option exists which will update the previously published post with the new text or create a new post if the edit calls for one.

Wrap-Up

This post actually really bootstrapped itself. The first version was a simple "This is my new feature!" post, which was written after the naive bit-field draft system was live on the blog. The process of expressing what the bit-field let me do lead me to realize that what I created was as much a bug as a feature, and while I wandered off down a rabbit trail and wrote the above visibility system before I finished this post, just writing it has helped me crystalize what exactly the feature I'm building should do.

TL;DR

Think carefully about the change you propose... it may not be the one you really want to make.