I’ve read the answers to Why is Global State so Evil?, and I think the negative consequences do not apply in this situation. However, that’s what everyone says just before they get hit by a falling piano and my software architecture lessons have been a few years ago, so…
We are developing a desktop chat application in Python. In that application, there is some inherently global state: account configuration, client classes (from the chat protocol library) for those accounts, open conversations, etc..
In the current design, we use single global instances for the Account controller and the Client controller (handling the client objects from the chat library we are using) as well as the Conversation controller (handling conversation abstractions for chat sessions including group chats). These global instances are available from everywhere via a simple import foo.app as app
. The controllers have models associated and read-only views on those models can independently be instantiated at various places in the application.
The following remarks apply:
- Unit testing works fine so far: we can easly mock away those global instances with Pythons
unittest.mock
(in fact, the instances are only initialised during actual application start up, so they areNone
otherwise -> instant and obvious error if not mocked but used in testing). - Modification of the global data state associated with the controllers goes through the controllers only; and modification of the state emits well-defined callback signals so that consumers of the state can obtain updates.
- The code has concurrency, but it is using Python’s asyncio: this implies co-operative multi tasking, thus the points at which other tasks can interfere are well-defined and obvious in the code (when we use actual threads, global state will not be accessed directly). So it is safe to assume that global state does not change within a (non-coroutine) method.
- Most uses of the global state are simple lookups of the type "Which Client object is associated with the Account X?", "Get me the conversation object on account X with peer Y", "Which accounts and identities exist?" and subscribing to updates.
Now I wonder: Have we overlooked anything? And if so, what would be the appropriate fix? I feel that this design could work well enough, but I’m afraid we might have overlooked anything relevant which will fall on our feet at some later point.