Deterministic Systems

Languages with a strong type system allow for nearly mathematic proof like safety when refactoring systems. The whole system can be seen like a mathematical system, everything is deterministic. You can know certain truths, like that all called functions do exists, without actually needing to run the system. Changes to a system component, e.g. renaming a function, will lead to a deterministic change of the overall system.

This makes changing the system, and thus refactoring, very easy. In the end you just let the compiler proof that everything still works.

Non-Deterministic Systems

On the other side are dynamical languages like javascript. They are more like empiricism. There is a hypothesis of how interactions with the system, like changing a method, is supposed to work, but you cannot proof, in a mathematical sense, that certain things are true.

One cannot proof that all called methods actually do exist at runtime. One can run the system to check whether they do, but from the fact that there are many branches to take, one can only know that this hypothesis is true, until it is not true anymore. So statements about dynamical systems are entirely non-deterministic. Therefore follows that refactoring in dynamical languages is entirely non-deterministic too.

Changing Non-Deterministic Systems

As refactoring non-deterministic systems is based on hypothesis and not truths, one should not do refactorings based on static assumptions. E.g. you cannot remove a class just because a search for it’s class name returns no results. The class could still be invoked dynamically from somewhere. For languages with strong type systems this would work. The compiler would even prove that the new changed system is still working after the class is removed.

Because we cannot do refactorings of non-deterministic systems based on static assumptions, we need a different set of tools. We need empiricist tools, tools to prove hypotheses wrong.

One such a tool is GitHubs Scientist. It allows to run experiments in production to see how certain refactorings affect the system. From the fact that we only know how the systems really behave when we run it follows, that this is the right way to change the system. Other ways, like static reasoning, are not taking into account the non-deterministic property of the system, and thus are far too error prone and risky.

Next time you are refactoring a non-deterministic system make sure to have the right tools in place. Tools like Scientist. Analysing the system at runtime in production is the only way[0] to really know how the system behaves.

Thanks for reading :) Follow me on twitter if you’re interested in more of this stuff!

[0] Well, unit and integration tests are another way to know how the system behaves. But this only works when you have enough code coverage and when the component you are refactoring actually has tests.