ForeverList update: Starting to implement transaction log as storage

Quick update on the ForeverList project: Today I spent two hours and implemented basic loading and storing of the ForeverList document as a transaction log.

First of all, its worth noting what “document” in ForeverList will be. ForeverList lets you create lists of notes, where each note is made up of text and possibly more child notes. This way, you can organize your notes hierarchically.

Now onto the transaction log: When you edit a ForeverList document, ForeverList will store individual changes as transactions in a log. A transaction is simply a description of a change to the document. For instance, if you create a new note, the app will store an entry in the log that says “user created a new note and here is the text they entered”. If you go and change the text in that note later, it will create a log entry that says “user changed the text for this note to ‘blah blah blah'”.

When the user saves the document, which is made up of many notes, the app will simply store this log of changes and not the notes themselves. If you close the app and later re-open the document, the app will load this log of changes and play it back from start to finish to re-create the document and its notes.

Why do it this way instead of storing the document’s list of notes verbatim? Well, using transaction logs will allow a document to be edited by multiple devices. That is, it will let you open up the document on multiple Macs or iPhones (once I write that version of the app) because each device will get its own transaction log. When you save from that device, it will only write the changes that were made from that device. However, when you open a file, we’ll take all the changes made from all devices and merge them into a mega log file and play back all the changes in chronological order. This is basically how I implemented the storage for Lil Todo.

Sharing a single file across multiple devices requires a lot of coordination. If your Mac and iPhone are editing at the same time, how do you let them save different copies of the same file? How do you resolve conflicts if one of them saves first and the other one notices? By breaking down the changes into a log with timestamps, we avoid coordination by forcing each device to write to its own file (nobody else is allowed to write to it!) and we avoid conflicts by letting the latest change “win”.

One important bit about how the document is stored: the document is actually going to be a directory that contains multiple files, i.e. all the transaction logs from all devices. (In the future, when you drag and drop images into the app, I’ll also store the images in this folder as well!) The way I’m handling this is via Document Packages on the Mac. Cocoa on the Mac allows you to write a Document-based app where instead of saving to a specific file, you provide a “document package” object which describes a list of files that will be written to disk instead of a single one.

At this point, I don’t have cross-device syncing support yet. In the future, you’ll be able to use a cloud storage provider like Dropbox or OneDrive. For now, what you will be able to do is save a ForeverList document to something like Dropbox and you’ll still be able to open and edit that document from multiple clients because each client will only save to its own transaction logs. If they all shared the same file, however, we would have to worry about different file versions and synchronizing access to the file and potential conflicts in the changes. Dropbox will take care of syncing the individual transaction log contents for you.

In summary:

  • Each device will record each change that is made to a document and save those changes to its own transaction log.
  • The ForeverList “document” will simply be a folder that contains multiple transaction logs, one from each device.
  • When ForeverList opens a document, it will open up the folder, collect all the transaction logs from all devices and then merge them into a single transaction log, which it will then “play” from start to finish to reconstruct the document.

Simple macOS app illustrating undo/redo and other concepts

For my ForeverList project, I wanted to learn how to use NSUndoManager with a list, so I spent an hour and put together a simple app demo. Check out my SimpleLists project on github. It illustrates a few basic concepts in Document-based macOS app development.

The app lets you create a simple list. You can enter new items and select existing items to delete one by one. You can save and load to a simple JSON array. As mentioned, it supports undo using NSUndoManager.

Career tip: Write down what you learn each day

I started a new job a few weeks ago and one thing I’ve started to do is write down every new thing I learn each day on the job. I have a simple text file that I add to whenever I find I have to learn a new technology or engineering practice. I’m finding it has some nice benefits.

One: I’m learning a lot of new things right now. This is a good thing and writing down what I learn helps me track how much I’m learning each day. Once I slow down my learning, which will inevitably happen due to deadlines or other factors, I can re-evaluate if it’s something that’s temporary or permanent (time for a change).

Two: Writing down what I learn has led to a virtuous cycle. To maintain my learning daily pace, I’m finding that I’m seeking out things to learn. If I’m taking on a task and there’s something I don’t fully understand, I will now try to learn it more fully instead of getting by with being ignorant. It’s the old “you get what you measure.”

Three: Most practically, writing down what you learn each day will make it easier in the future when you look for a new job. When it’s time to find a new job, you have a long list of skills you’ve developed. You can turn those into appropriate bullet points for your résumé or talking points for the interview.

If you want to give this a try, just create a text file for your log. Each day, jot down the date and then when you learn something new, write one sentence describing it and move on. Don’t get into details. You just want to jot down something quick so there’s no friction to your current daily workflow. At the moment, I’m finding I’m learning about two to three new things each day in my new job. If you give this a try, let me know how it works out for you in the comments below.

ForeverList Update: File loading and saving

As mentioned the other day, I’m working on a new app. I’m calling it ForeverList for now. I’ll be posting updates to this blog as I make progress.

Well, today I added file load and save!

It took about half an hour to add. It was pretty simple to implement. All I do is serialize the tree representing the document into a JSON dictionary.* Just use JSONSerialization to get this done. Easy.

However, I then spent another half hour tracking down a bug caused by a poorly configured storyboard. (I have no idea how it got in that state.) I’d hit an exception at runtime, but other than a call stack into Cocoa, I had no way of determining what was causing the problem.

After pulling my hair out, I decided to delete the old view controller in my storyboard and add a new one, configured exactly the same way. That fixed it. 😩

I’m still seeing another exception in -[SFLList removeItem:], but it looks like another Apple bug. Argh!

Oh yeah, at one point, Xcode crashed in the middle of typing some code. This is one thing I often forget about Mac and iOS development: a lot of time is wasted dealing with issues in Xcode or Cocoa.

* I should note that the JSON format here is temporary. I’m planning on saving a journal of changes as I did with Lil Todo. That way I can allow multiple clients to modify the same document and sync them to a central file share, like Dropbox or OneDrive.

Software development: How to prioritize your work

Photo by the author

tl;dr – Work on tasks that will eliminate risk and reduce unknowns first.

Software development is a complex and unpredictable activity. Because there are a lot of moving parts, big problems may not present themselves immediately. Many technical challenges often don’t come up until other supporting work is done first. Usability issues may remain unforeseen until the bulk of an app has been written. These challenges can affect the schedule of the project if not tackled early as they can cause unnecessary re-work to be done.

Many of these challenges remain unknown simply because they are needlessly prioritized behind other work. Endeavor to constantly reduce risk by finding and completing high-risk work items first.

When breaking down your work into bigger pieces, assess the risk of each piece. Figure out what has the highest risk. These are the things that you understand the least. Ask yourself how much you know about the problem space. How many assumptions are you making? Are they valid? Are there a lot of moving parts? If so, can those parts work well together?

After coming up with a risk assessment, prioritize the highest risk items first. The goal in attacking the high risk tasks first is to learn and gather more information and then adjust your schedule, your designs, and your product goals as necessary.

Likely the biggest risk most products will have is the usefulness of the product itself. Is it something people actually want? How do you know? One way startups mitigate this huge unknown is to build a prototype (with a limited feature set and with basic UI) as soon as possible. Get it in your hands and potential customers’ hands early. You’ll likely find issues right away that you couldn’t see with static design mockups.

Another big risk is architecture. How should the product be built? If you aren’t familiar with the technical space, prototype using components as soon as possible. The goal is not to come up with a pristine architecture, but to just understand the problem space and see what is even possible with the tools and components available. Find out what the tolerances of each component are. Will it be enough for your goals? If you were planning on using open source components, do you have everything you need? The worst thing you can do here is to design at too high a level with limited knowledge of the space. Don’t commit to big decisions based on missing information.

In my experience, getting an end-to-end scenario working reveals so much. Plumbing, i.e. getting data from one place to another, is one of those things that is hard to theorize about and is only really knowable by writing code. Once you get a basic end-to-end scenario that uses most of the pieces of the stack you expect (even with prototype pieces in the middle), you will find out what components you’ll need and how to difficult or easy it is to connect them together. This will reveal more than a theoretical design ever could.

High risk work has a greater impact on the success of a product than anything else. It also means it’s harder to reverse these decisions, so it’s important to get them right earlier than later. As you complete more and more high risk work, the stability of your completion date should improve. How the final product and final software design will look should stabilize as well. Don’t let high risk and difficult-to-reverse decisions be made late in the project. Get those out of the way early!

In summary, map out what you need to do to complete your project. Figure out where the big unknowns (and big risk!) are and then prioritize those items first. Prototype as necessary to help validate your hypotheses, learn, and adjust your plan as you learn more. Repeat until you are done. Good luck!

 

These thoughts come from my own work experience, but many of the ideas weren’t obvious to me until I read Lean Software Development, so please read that for a more information.