Dynamic Programming Languages
I loathe dynamic programming languages. This week, in a system that spans 3 microservices, I needed to add two new fields to an object. I’ve spent at least two days tracking down references to said object and making notes on all the functions that may be affected by this change. I wish I had a compiler to validate type information at function boundaries. But I don’t, because the microservices are written in weakly-typed (aka dynamic) languages.
Because the services are written in dynamic languages, the only way to know what object is passed into a function is by looking at all the parent functions in the callstack as well sibling functions that may have transformed relevant data. The function that motivate this blog post has required me to look at six different functions so far. I have two more functions to trace before I can create the desired patch.
There is a cultural problem at play that is reminiscent of Tragedy of the Commons. For one to make a change quickly and confidently, the codebase needs high test coverage (shared resource). However, individuals are incentivized to avoid writing tests: People are rewarded for completing tasks quickly. What exacerbates the problem is that when a change results in a bug, it is frequently the person who made the last commit that gets the blame, rather than the past decisions that led to poor test coverage in the first place. It is not a fully formed Tragedy of the Commons problem as individuals are not rewarded for harming the shared resource, but is sufficiently similar.
Of course, one can use a combination of tooling and carrots-and-sticks to “ensure” the codebase has high enough code coverage – to avoid requiring programmers to carefully scrutinize the entire call graph. At this point however, the amount of work that goes into this is likely the equivalent of using a strongly typed programming language.