With the latest version, we overhauled an existing Ulysses feature that could cause glitches in practice: the writing session history. Götz, the developer in charge of this feature, gives his insight into the problem’s origins, the chosen approach to solving it and the process of implementation.
Götz, you reworked the so-called writing session history. What kind of feature is this, and for what is it suitable?
The writing session history allows you to look at your writing progress over the last few days for each group. You can see how many characters, words or pages you’ve written, your daily average, your “high score” and your progress within the current week. We offer this session history for each group that has a goal attached to it.
Another feature, the ability to add writing goals, is based on the writing session history. You can set yourself a daily writing goal, e.g., “write 1000 words per day”. This can help you build habits or simply force you to work on your diary, novel, thesis, or whatever project you’re writing currently.
What kind of problems occurred with this feature, and where did they originate?
In a number of situations, the daily counts for the session history were inaccurate. These wrong counts affected both the history as well as daily goals. For instance, this could occur when moving around larger amounts of text between sheets or groups. Or when iOS removed sheets from the local iCloud storage since it needed more disk space and then re-added them later.
The problem originated in our approach to counting and saving the text statistics. Originally, these statistics were gathered on a per-group basis and stored as total counts. However, due to the above problems, the values could be wrong. For example, if the iCloud system service removed a sheet in a group due to low storage, the count would be lower. If the user restarted Ulysses on the next day and the system service re-added the sheets, Ulysses would count the contents of the sheet towards today’s statistic, which is wrong.
How does your new solution differ from the old one?
In our new approach, we store the statistics separately for each sheet. So for each sheet we store how many characters/words/pages/… have been written on each writing day. When viewing the writing history for a group, the session history is now calculated dynamically based on the individual session histories of its sheets. That means, to calculate the session history for a group, it looks at its sheets and aggregates the counts for each session of each sheet. This solves the problems we had with the old solution.
Of course, this solution comes with some trade-offs. For instance, when deleting a sheet, its writing history is also removed from the group. When moving a sheet from one group to another, the writing history is also “transferred” to the other group. This might or might not be intended by the user. Nevertheless, we think the benefits of the new solution outweigh the potential confusion when deleting or moving a sheet.
Please describe the process from idea to implementation so that non-developers can understand it.
At first, we needed to collect all the known issues of our original problems — in which situations could the writing history report invalid values? Could we do a few tweaks here and there to alleviate the issues? Or do we need to overhaul the whole concept and rewrite it from scratch? It became clear that our original approach was flawed, and we needed to move the storage for the statistics away from the groups and into the sheets. We quickly figured that this would be the only sensible way to go.
We went through all kinds of probable and improbable scenarios we needed to consider (the aforementioned move and delete operations, handling material sheets, Markdown sheets, resetting daily counts, duplicating sheets, and so on).
Then the actual implementation phase began: removing the old code, adding the new counting approach, testing and finally code review. Afterwards, we gave the new version to beta testers to check if everything was working fine. We needed to work out a few glitches which we didn’t take into account and were able to ship the new session history in a recent update (23).
Some users are not aware that developers of quality software constantly need to put time and effort into its maintenance. Would you please explain why maintenance is crucial and how significant the overall share of maintenance is in Ulysses’ development?
Fixing bugs is a constant part of our work on Ulysses. Occasionally, a bug goes unnoticed during development and needs to be fixed in an update. When macOS or iOS are updated, we also need to check whether Ulysses still works fine. Existing macOS or iOS versions have bugs, for which we need to find workarounds. These operating system bugs might be fixed over time, so we can remove the workarounds. Each (major) operating system update also brings a slew of user interface changes, which we need to adapt in Ulysses. Other third-party software such as WordPress or TextExpander also change their API from time to time. Finally, we need to rethink conceptual decisions in Ulysses from time to time. The writing history was one of those decisions which turned out to be flawed. It’s important to make sure Ulysses is running smoothly, since it’s being relied on by our users as their daily writing tool.
I’d roughly estimate the share for maintaining Ulysses to be around 50% of our work; it has definitely increased over the years. The app has become more complex, with more features to maintain. Moreover, we’re taking bug and crash reports as well as user feedback in general seriously, and we’re always eager to deliver great quality.
Thanks for the insights! Do you want to share which tasks are up next for you?
Ulysses’ development team pursues multiple projects of varying complexity and scope at a time. My current focus is on updating Ulysses for the latest operating system updates: macOS Monterey and iOS 15, together with two colleagues. There are a few features which we’d like to integrate into Ulysses, for instance support for Shortcuts on macOS. As always, we also need to ensure that Ulysses looks great on the latest updates and supports all device features.