Types and Programming Languages Part 15 – Prohibit vs Enable in the software engineering

This is the fifteenth part of the Types and Programming Languages series. For your convenience you can find other parts in the table of contents in Part 1 — Do not return in finally

Discussions in software engineering are pretty often about taste. Even though all debaters argue based on their knowledge, skills, or experience, their discussions quite often come down to the matter of their personal preferences.

One of the examples for such a discussion is Open Inheritance vs Designed Inheritance by Martin Fowler. The question is: should we allow for a class to be inherited or should we block it by default. We can provide multiple arguments for and against, but there is no single metric showing which approach is better. Same applies to making methods virtual or sealed by default. Or about making everything private or exposing details. These discussions are generally based on the attitude of the debaters, and it may be quite surprising to see the mechanisms behind that.

Let’s take this very distant example. Imagine that you are coming back home at night, in a dark alley, and then some people show up, punch you a couple of times, take your phone, and disappear. You can generally take three different approaches after such a situation:

  • You decide to avoid such an encounter as much as possible. You don’t come back home at night, avoid dark alleys, or even stay at home. Basically, you make yourself avoid the situation.
  • You argue, that such a situation shouldn’t occur, that police officers should be on the street at night to help you in such case. You make others to make sure that situation like this will not happen again.
  • You can also go to gym, learn boxing or martial arts, or carry a weapon, to make sure that next time you’re prepared, and you won’t let them take your phone. Basically, you make yourself embrace the situation.

It’s up to you which solution you pick. You may argue, that some of them are undoable in your case or are unpreferred. The same applies to software engineering. Let’s say that you found a bug in a lengthy stateful function. What do you do? Options are:

  • You decide that you and your team will not write such functions anymore. You pay attention in code reviews to not let these functions pass through.
  • You decide that such functions shouldn’t be allowed at all. You change your programming language to something with no stateful functions (like Haskell).
  • Or you just improve your skills to be better in maintaining lengthy stateful functions.

It seems like the industry typically goes for the first option. You can read a lot on the Internet about “clean code”. Principles like “a method should have at most two parameters”, corroborated with statements like “I’m in the industry for 30 years so I know better”. However, at some point it’s just a matter of personal preference. The preference may be affected by cognitive skills, environment, experience, proficiency, risk, cost, and perhaps many more reasons. However, there are no metrics showing that “DI containers are bad” (it’s surprising that Java programmers criticize DI often, while .NET programmers get DI support out of the box with .NET Core), “functions should be at most 10 lines long” (and then you see pull requests to the Linux Kernel doing completely different), or “pure functions are better than impure ones” (and then you juggle your code like crazy to represent a state machine with no state in it).

Should we prohibit or enable? I believe the latter. But again, that’s a matter of preference.