On developing for Desktop vs. Web
I recently spotted this article ("Should You Develop a Desktop or Web App?") and decided to share my own experience. I built my last desktop 2 years ago and I can definitely agree with the points in the article but I would like to add my own opinions too.
For a startup or a small business, the two most important factors of deciding a desktop vs web app platform are:
- Does the platform support your core business?
- Do you have the people and funds to build and maintain a desktop application and the required infrastructure around it?
These can be more-less objectively evaluated. For the KAM project (lots of data-driven forms and views) we also had to make this decision. The first factor was OK. Data-driven forms and views can be built on both platforms. However, we grossly underestimated the effort and cost for the second factor, especially the infrastructure part. There are other decision factors as well:
- ease of use
- possibility to use offline
- familiarity to users
- nice fuzzy feeling of downloading and "owning" something
With no clear perspective on the build and maintenance costs we made our decision by these. It slowed us down, especially in the later stages with all the supportive apps, webpages, etc. piling up until the funds ran out.
This does not mean that we would have been better by going with the web application. The project halted at the internationalization process with no clear idea how to sell (yet another) health/fitness app to an international market already saturated with similar apps/sites.
Developing for desktop: extra costs
When you develop a proprietary desktop application, you will need the following:
- Graphical toolkit
- Non-GPL lisensed libraries
- Access to most (all) platforms you plan to support
Distribution channel (with update support)
- Copy protection
- Antivirus whitelist
You might also need:
- Multiuser support
- Synchronization to cloud
- User tracking
And sometimes you have to debug all of this, remotely, somehow.
Compared to web frameworks, graphical toolkits are more likely to be proprietary and have license costs. Example is Delphi 10: the Professional license costs 1243 EUR. It supports Windows and Mac but not Linux.
There are also Open Source toolkits. The best one is Qt. It supports lots of platforms. This is what we ended up using. The only downside was C++ which I had to learn from scratch. C++ is a beast but fortunately there were some very good Qt books that covered the basic C++ too (the subset needed for Qt).
You cannot use GPL-licensed libraries in closed-source applications and you cannot use LGPL-licensed libraries in statically-linked applications. Static linking produces neat single-file binaries that make distribution and deployment considerably easier.
Library licenses can also change during development. This happened to us with the QtJson library. Fortunately, we did not have to use a version with the new license and later switched to Qt5 which had built-in JSON support.
GPL libraries can be freely used in proprietary web applications. For desktop, you would have to reimplement the functionality that is only available in GPL-licensed libraries.
Access to platforms: licenses & hw
When you support Windows, you are likely to need the following:
- Windows XP 32-bit
- Windows XP 64-bit
- Windows Vista 32-bit
- Windows Vista 64-bit
- Windows 7 32-bit
- Windows 7 64-bit
- Windows 8 32-bit
- Windows 8 64-bit
- Windows 8.1 32-bit
- Windows 8.1 64-bit
- Windows 10 32-bit
- Windows 10 64-bit
This makes 12 systems to be installed. XP is not even supported by Microsoft but your users still use it. Could this be done by less number of systems? Maybe. We built on 32-bit Windows XP (MS takes compatibility seriously so later systems are likely to run stuff built on older system). We installed other systems on-demand when an user had issue on the specific OS version.
There was also a clean Qt-free Windows XP installation to test bundling of DLL files. It comes out that Qt can install some DLL system-wide and the application dies with an horrible error when those DLL files cannot be found.
For some time there was also a separate Windows XP machine doing Continous Integration builds but we gave up that once builds started to take 40+ minutes on it.
Windows versions can be installed into a virtual machine such as VirtualBox. On the other hand, when you support Mac OSX, you are likely to need an expensive Mac with the latest OSX installed. If you have other uses for Mac, it might justify buying one. It's also possible to force OSX onto VirtualBox by some technical hacks and a license violation (to discover that OSX+VirtualBox filesystem is so slow that build takes 2 hours...).
Installation of desktop software is very platform-specific. It is supported in a sane way only on Mac OSX. With Qt you generate a bundle with the macdeployqt tool. The user will download it and drag the file to their Dock. The bundle contains all relevant libraries.
On Windows you have to provide your own installer. We chose NSIS for building our installer. It comes with its own language which has to be learned. The installer copies files into the correct place, creates launch links and sets up the registry for uninstallation feature (which is implemented through generating another binary that removes the files and the other stuff).
Linux has almost no support for binary applications. We went with a route of providing a bundle with binaries and all Qt libraries and we supported
32-bit systems only. The application files were placed into a tarball that the user had to manually extract. To launch the application, a shell
script had to be executed. The script sets up
LD_LIBRARY_PATH for the binary to pick up our Qt version. This is quite inconvenient for
both users and support (the support must be able to explain how to do it on any possible Linux desktop environment). The issue with binary
applications on Linux has also
by Linus Torvalds himself.
You will need an e-commerce frontend for users to be able to buy your application. The best way to do this might be using some hosted service solution. We did not find anything that supported all at once:
- Software downloads
- Installation keys
- Custom design/branding
- After-payments (järelmaks in Estonian)
- Support for Estonian Banklinks (horrific non-standard way to accept the payments over Internet)
The solution was to build it ourself.
We implemented app keys. User obtained a key when purchasing a copy. The key was sent to an HTTP endpoint during installation and app starts. We monitored the keys for duplicate installations but never used the power of revoking them although we had support for that built-in. In one case we contacted the user. There are no perfect copy-protection or DRM schemes and lots of effort can be wasted on an attemp to build one.
In the best case your application updates itself. You need a protocol to check for updates and execute them. Doing requests over HTTP is easy (over HTTPS it is actually not easy because you need some DLLs you cannot distribute yourself) but executing updates is again very platform-specific.
In the KAM project we did an HTTP request every time the application was started. The request downloaded a JSON file containing the latest version number. When the latest version was greater than the current version, we showed an update notification in a corner of the main screen. The notification just opened the link for the new version download in the default system browser. The update was completed by installing the new version from using the installer mentioned above.
Distribution: are you a trojan?
During our first release we started receiving reports from some users that they cannot download or install our application. It took a day to figure it out: antivirus products were blocking our application download. How do they block and how it appears to the user? It depends on these things:
- Browser version
- Windows version
We had to whitelist the release with all popular antivirus products. All of them accepted us in 2-3 day but the issue definitely had made the launch go less smoothly.
Extra feature: multiple users
At the first we implemented the application to store its database file in the home directory (or equivalent) of the current OS user. This turned out to be not enough. There are many desktops with a single OS user shared by whole family. Our most demanded feature was support for multiple users on a single OS user. It was actually quite easy to implement for this application but it might get quite complex and might require additional features like password protection (how to implement password reset for such environment?) and encryption.
Extra feature: user tracking
For web sites you have Google Analytics with nice support for custom events. On desktop you have to either rely on a 3rd party service that you integrate with the app or build it yourself. At first we tried to integrate with Google Analytics directly but they required always-on connection (no bulk event data upload). In the end we built another API and a web endpoint to collect bulk event data uploads and display analytic data. We were only interested in the usage statistics of various app features.
Extra feature: offline synchronization
Some users had multiple installations and wanted to share data between. The multiple installations must be able to share and merge data periodically. As you cannot connect arbitrary user computers (because of firewalls, NATs, etc.), this required another central point to synchronize with.
We managed to implement this system in the KAM project and bolt it onto the existing database and user's data. It took 6 months to build and was the last major feature we implemented before running out of money. It comes out that multi-master consistent replication is not a trivial problem to solve.
Nightmare: remote debugging
Sometimes the application crashes for users. It does not happen for all of them (so that you have a nice repeatable bug!) but only for 1-2 of them. There are many reasons why an application might crash or behave weirdly:
- Database corruption
- Hardware errors
- Windows virus
- Windows antivirus
- Program code corruption
- Library code corruption
- Hard disk is full
- Database has lost write access
- Out of memory
- Out of file descriptors
- User has "tweaked" some files
Good luck debugging these remotely on a normal user computer!
Extra feature: crash reported/debugger
We had to build a reporting tool to make debugging easier. The tool worked only when the app did not crash right away but only behaved weird. The tool required another web endpoint to receive the data. We sent the Operating System name (normal users do not know these details) and the version and the whole database.
We got some issues solved with these but there were some unresolved crashes left. We were never able to reproduce them even with the full database dumps. Yet for those couple of people this had completely killed the usability of the app.
Go with a web application unless your core business requires a desktop application and cannot be realized without one.