This post was originally published on the Doctave blog.
We took the arguably unusual choice to build a desktop app for documentation. If you look around, documentation tools generally fall in 2 categories: open source CLI tools, and cloud-based WYSIWYG editors. In this post we'll talk about the reasoning that lead us to build a desktop app instead, and how it actually works.
What is it for?
Our desktop app allows users to preview what their documentation will look like once published. It also catches errors such as broken links or syntactical problems.
Users can open our app next to their editor of choice, edit their Markdown documents, and the app will refresh on every save to reflect the latest version.
When an error is detected, the user is immediately notified, and clicking on the link in the error takes you to the page with the issue.
While my opinion is obviously biased, this fast feedback loop is amazing to work in. It takes the app milliseconds to do a complete re-render of the project, so you're able to move essentially as fast as you type.
Why a desktop app?
Doctave is built with
in mind. As said above, there are two popular approaches for documentation tools: CLIs and cloud-based WYSIWYG editors. The point of docs-as-code is working on local files in source control so the cloud was out immediately.
So this leaves the CLI option. There's nothing "technically" that would have stopped Doctave from having this interface:
$ doctave serve . Serving your docs at http://localhost:9090 ...
But here are the negatives for requiring a CLI.
There has been a resurgence of desktop applications in the past few years. This is likely partly due to technologies like Electron making it easier to ship applications built with web technologies as cross-platform desktop apps, but it's also arguably
because the user experience can be better.
Our users love that the preview and error checking happen in one place. You don't have to spin up terminal sessions and swap between an editor, a terminal, and a browser.
Finally, updates become trivial. Tauri's built-in update function has worked flawlessly, across 3 operating systems, ensuring our users have the latest and greatest version with minimal headache. You get a pop-up every time there's a new version, and the update happens automatically. There isn't really a universal way to update CLIs.
A lot of people working with technical documentation are not developers themselves. They are still technical, but in roles like tech writers, product managers, or support agents. They are comfortable on the command line, can use Git, but do not want to spend time understanding why their version of OpenSSL does not match what is expected.
If you are expecting your users to setup a complex development environment to run your tool, you are alienating this important group of users. Even developers get frustrated when small environment differences cause
npm install to fail!
There are ways to mitigate this, such as building static binaries. But you still have the problem of getting your users to update your SDK when you release features, you have to support different package managers, you have to make sure your software is not flagged by the OS as malicious, and much more.
A common comment we hear from our users: it's just easier to use Doctave.
How does it work?
As mentioned above, Doctave uses Tauri as the shell of our application. Think of it as a lightweight and security-focused Electron. It's a very interesting project that I highly recommend checking out. For our purposes, here are the 2 key features that are interesting:
- It uses each operating system's native web view, making app bundles significantly smaller
- You can code your local "backend" in Rust which you can invoke from inside the web view
(NOTE: this is not a "web server backend". This is code running natively
inside the local Tauri process.)
Doctave's core documentation project parsing code is written in Rust and we call it
libdoctave. It takes as input a bunch of files and creates an in-memory structure that you can use to render a Doctave documentation site. It is used both in the desktop app and on our web platform, written in Elixir, via Rustler.
This is how we are able to share code across the desktop and the web platform. We can render the same content identically regardless of where we are.
Rendering a page
As an example, let's walk through what happens when Doctave renders a page you are editing.
First, Doctave is running a thread inside our local "backend" that is monitoring filesystem events. If it detects a change, it triggers a refresh of the project. Here's how that works:
- The backend re-reads all the project files from disk
- It feeds those files to
libdoctaveto create an in-memory representation of the project
libdoctavefirst runs a
buildstage, to check the project is syntactically correct
libdoctavenext runs a
verifystage, where all pages are checked for issues
- Finally, the requested page's HTML is serialized and sent to the frontend to display, along with any errors
This all happens in single-digit milliseconds every time you edit a file.
As you can see, there is still a lot of room for optimization if ever required. We could be smarter about incrementally updating the
libdoctave in-memory representation. We could reduce IO by only reading the files that have changed.
So far, however, that has not been necessary.
Desktop apps rock 🤘
Betting on Tauri and having a desktop app in general has been a great decision. Especially with our choices of technologies, namely Rust, this combination has been a delightful if somewhat unconventional tech stack.
Every startup has a certain number of "innovation tokens" that you can use on risky and/or new technologies. In our case, this was a great investment.
Top comments (0)