Note: This post is part of the series Lessons (re)learned.
Every system has sources of nondeterminism. We need to identify them and push them off to the boundaries, leveraging dependency injection in order to make use of them. Not controlling these sources of nondeterminism usually leads to test flakiness. Time and random are two very common sources of nondeterminism. In fact, any language-provided static functions are likely to be nondeterministic. Third-party integrations are definitely sources of nondeterminism.
Dave Farley, from Continuous Delivery, released a really nice video titled "BDD Testing Time". The technique demontrated there can be applied to other sources of nondeterminism as well.
This idea flows naturally if we're practicing TDD. It will have a nice side effect of making our codebase more modular and composable.
Gary Bernhardt has a really nice screencast titled Functional Core, Imperative Shell where he presents a very interesting way of thinking about this.