Deterministic Core, Non-Deterministic Shell
Fourteen years ago, Gary Bernhardt introduced the concept of Functional Core, Imperative Shell. This idea, though not entirely new, gained clarity through his conception and provides a useful framework for discussing testing and determinism in existing systems. The model divides code into two parts - the Functional Core, which is purely functional (no IO or destructive state updates) and focuses on business logic, and the Imperative Shell, which has less pathing but maintains state, coordinates dependencies, and handles outside interactions like IO (such as writing to a database or updating a GUI).
The clear distinction between these components makes the core more amenable to testing since the same inputs always produce the same results, and there's nothing to mock or stub. Additionally, the core's complex business logic aids in understanding system behavior.
However, strictly following pure functional programming may not always be practical due to language or performance constraints, such as in C. In such cases, weakening the requirement to merely deterministic can retain the testability benefits of the Functional Core, Imperative Shell model while increasing its applicability. This approach focuses on identifying and separating non-repeatable behavior, which is typically found in the non-deterministic shell.
By recognizing and addressing these issues, developers can gradually improve the testability and reliability of their software, much like defragmenting files to improve read speed on a hard disk.
Written by urgent.news from Lobsters's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.