Not Everything is a Web App
23 December 2025
Over the years, I've come across more than a few projects where web technologies
were used unnecessarily. In all of these instances, all communication between
components was performed over localhost via HTTP REST API or WebSocket with
some sort of RPC API (e.g. gRPC, JSON-RPC). None of these projects
had a technical reason for such architecture so my assumption is always that the
people before me did not know any better.
What to do instead?
Design with library oriented architecture in mind - implement the business logic of in reusable libraries. Here are the common scenarios of deploying your software:
- All logic is hosted on the same machine and in a single process
- combine libraries into a single executable to get the desired functionality
- Logic is hosted on the same machine, but spread across different
processes
- combine libraries into multiple executables that communicate with some sort of inter-process communication mechanism (e.g. D-Bus)
- Components are hosted on different machines
- wrap component libraries into executables that expose each library functionality in the desired way (e.g. REST, RPC)
Naturally, in the second and the third scenarios, multiple libraries can be part of a single executable. The flexibility of this design allows you to retain the optimal performance for a given scenario.
Finally, this design inherently pushes tests from the executable boundaries to the library boundaries. The resulting test suite therefore has most of the business logic covered in the component libraries tests while the I/O is tested in the component executables tests. Usually, most of the component library tests can be I/O free and thus fast, non-brittle, and trivially parallelizable.