Peter Marklund's Home
Today I fixed a bug in a Python API that I have been working on lately. It occurred to me that instead of just fixing the bug I should reflect on why it happened. The bug was in a function called set_model_defaults. In this API, the name of a model is usually inferred from the filename (this is the default name) but the name can also be specified explicitly as an attribute in the model itself (and this should then override the default name). The bug was that the overriding of the default name didn't work. I had two reflections on why this happened:
- Precise names can help prevent bugs. Once I changed the argument name from "name" to "default_name" the bug became more obvious
- Configurability/flexibility of software drives complexity and may result in unused/untested branches and those are often a source of bugs. In my case the ability to override the model name was simply a feature that I wasn't using much so it was not well tested and thus the bug didn't show up immediately.
- Bugs usually happen in the periphery of your application, i.e. in the less tested and used features or in the edge cases
- Silent failures/missing assertions
- Too loud failures/missing error boundary
- Missing null checks (being defensive and checking for missing data at runtime). Your static types and compiler may indicate that a property cannot be null but that doesn’t guarantee that it’s not null at runtime - an external API may for example feed you data that is incomplete or invalid.
- Missing timeouts, too short or too long timeouts
- Missing retries (network issues etc)
- Not good enough validation
- Not good enough testing
- Too tight or too loose regular expressions