Are you in awe of the Jedi programmers who seem to produce bugless code? Are you bewildered by the Guru programmers who fight inefficient code with their hands tied and eyes closed? They are not superhumans; these are mere mortals who have a repertoire of bug patterns in their heads owing to their experience. They have also mastered behavioral traits that aid in detecting bugs and flushing out inefficient code.
One can avoid the majority of bugs by adopting two behavioral traits.
- Taking a step back and asking – What can go wrong?
- Asserting your assumptions.
Let us work with an example.
The below code accepts a list and returns the first element.
def get_first_elem(lst): return lst
What are the implicit assumptions that you see?
- No one will pass a null list.
- No one will pass an empty list.
What can go wrong?
- If someone passes a null list, the code errors out.
- If someone passes an empty list, the code errors out.
The idea is not to code defensively but to be aware of the assumptions and error conditions. It might as well be that the function should error out when someone passes a null or empty list; when it happens, it should not be a surprise.
Adopt these behavioral traits whenever you read or write code; you will be miles ahead of the rest.
Some of the other common bug patterns follow.
Never let it leak
Not closing opened resources – be it file descriptors, database connections, HTTP connections, or socket connections. Programming languages have constructs to do this – finally block in Java and Python, defer in Go lang. Whenever you open a resource, close it; never let it leak.
When establishing a connection to an external resource, be it a database or a remote server; configure appropriate timeouts. Being stuck for an undefined period establishing a connection is not a happy place to be in; fence the connection establishment time to a reasonable value. Also, timeouts come in various flavors – connection establishment timeout, socket timeout, HTTP server timeouts. Familiarise yourself with all that apply to your scenario.
Keep an eye on runaway resource creation. There is a limit to the no of connections that an external system can handle; there is a limit to the no of files one can create on a file system. Be aware of these limits and put in checks and balances to bound the creation to acceptable values.
Reuse over recreate
If you are opening a connection or creating an object repeatedly, check whether it is possible to pool the resources instead of repeatedly re-creating. Create a pool of resources once and then reuse when needed. This principle applies to all sorts of connections – HTTP, socket, database.
Using a resource pool alleviates the boundless resource creation problem too.
Encoding it right
While working with text, take care of character encoding. Things work great until one beautiful day someone passes a foreign text to your code, and everything collapses like a house of cards. Familiarise yourself with character encoding and take care of it while coding.
Stand on the shoulders of giants
If I have seen further, it is by standing on the shoulders of Giants.
– Isaac Newton.
The three chief virtues of a programmer are: Laziness, Impatience and Hubris.
– Larry Wall.
Doing everything yourself and not delegating to established libraries, frameworks, and tools is the root cause of a large number of bugs. In all probability, someone would have faced the problem that you are facing and crafted a well-tested solution to it – shamelessly use it. There is a reason why great programmers claim laziness is a virtue – follow this in spirit and practice.
Grokking these bug patterns and adopting the behavioral traits will make you a coding Yoda, who does not want to be one?
An earlier post on similar lines on Software Security.Follow @abhyrama