My current project is using a ASP.NET MVC API with an EmberJs front-end. All of the API endpoints return dynamic objects to support the proper JSON format for Ember.
When writing unit tests, we often hit the end points directly (to test the entire back-end stack). Our tests then take the dynamic wrappers and unwrap them so the tests can use the actual object. One problem we encountered is that the tests don’t “compile.” If we change the internal object of the API dynamic wrappers the code will compile just fine, but when the tests are executed the code will break. This is an exceedingly frustrating situation caused by dynamic types at the API endpoints.
While it is easy to dismiss this issue as a non-functional issue (since it only applied to tests) unit tests are a cornerstone of good software development. It would be much nicer if the tests would just compile and do the appropriate type checking during the build process instead of blowing up at runtime.
Consider a code structure with repository layer that is essentially an SQL query wrapper and a query layer which calls the repository and other queries to rebuild complex, nested data structures. The required use case is that the Query class for a specific entity is the only query allowed to call that entity’s Repository. Luckily C# is an object orientated and compiled language. We were able to enforce this requirement with a sexy inheritance trick. The query classes extend their respective repository and the repository methods are all given the protected access modifier (accessible by sub-classes). This way we build a compile time enforcing of the repository query interaction rules.
This code structure identifies many hidden bugs where query classes directly call repositories that do not build out the object structure in the correct way. In addition, this structure is much easier for new developers coming on to the project. Instead of having to read documentation (if it exists), or spend needless hours debugging the developer is given a compile time error that he is calling a function in the wrong place.
While this structure is possible in dynamic languages like Python, leveraging the compiler to enforce coding rules is a huge advantage that compiled languages have over dynamic languages. When used effectively the compiler can be extremely powerful.