Basic Principles of Writing Cross-Platform Code

Note: The content below is relevant to some other languages, though here we're mainly talking about Go. A reasonable amount of simplifications is in place. Pay attention, and think about what you read.

In one sentence, writing cross-platform code can be almost indifferent from writing for a single platform, if you've done the homework, i.e. have learned and followed good design and architecture principles and rules. The principles we're referring to are SOLID, some of the design patterns and common sense. Otherwise, if the good practices are neglected, chances for success are dramatically lower, or even zero.

As a general rule of thumb, all the business logic must be platform-agnostic. It is the implementation details what varies from platform to platform, but core algorithms remain common for all of them. This is important because even two platforms significantly increase the complexity just because all potential outcomes are now squared. Any design decision should be aligned with the main goal – reducing entropy in the software.

The starting point, and an obvious way to reduce entropy, is to keep the amount of cross-platform code at a possible minimum. The less code is different between platforms, the easier it is to guarantee its correctness, maintain and test it.

With the two principles applied, the likelihood of a project ending up in a better shape is higher. Though it's still possible that the project might not be able to achieve its goals due to specifics of one of the supported platforms. If it is the case, there are essentially two potential scenarios. One is trying to maintain all features on par for each platform. While this is the ideal and desired situation, it may often lead to waste of resources since not everything is equally possible across available platforms.

The other possible scenario is a reasonable and pragmatic trade off. What helps in staying efficient is to know when to mock missing cross-platform features or skip them at all. Not every system feature has something similar in another system.

For example, there is no direct alternative to Windows's Registry in Linux or FreeBSD. Of course, in Unix, Linux-based systems and its derivatives we have a kernel and the sysctl API or similar, but that's not exactly the same thing is the Registry (One may argue that systemd has been working hard to become a Linux counterpart of the Windows Registry). Trying to mimic its behaviour would be tedious and unjustifiably complex. In such case, two potential alternatives are available – one is to abstract the business logic such that it does not depend on a particular system feature, and the other is to skip one system that does not support some functionality that is important in another system.

To get a sense of how implementation details can vary across many platforms, have a look at the list of differences in the net package in the Go standard library.