Good function definitions or why interfaces matter
Why thinking about others makes you a better programmer
College education misses out on one very fundamental part of programming - it is a team exercise. Rarely, if any, individual project exist. The implications of this are manifold, primary being - just writing code is not the job, it should be easy to understand so that team can review it and reuse it.
Once you take into account this, everything we do in coding changes. We cannot have variables name like a,b,x. We cannot have a single function with 1000 lines and we cannot have a single file with all the code.
Contracts become important too. You are bound by those as other team members start using those contracts to build their services.
The current post covers a simple yet important aspect - Functions
Consider this function -
def process(self, channel, method, properties, body):
data = json.loads(body.decode('utf-8'))
for d in data:
...
Just by looking at this function definition, it is hard to tell what does body contains. It can be a string, a number, an object or like in the above case - an array. The function get weirder as we go further as data is not just an array, it is an array of Objects. Now, if anyone wants to use this function, he will have to first read the code to see what is the object structure and what all keys are mandatory/optional.
Having proper data types and object definitions helps in understanding the function definition better. There are some other things to keep in mind while writing functions -
Function ideally should not take more than 5-6 arguments. If there are more than 5-6 arguments, it would be doing more than one thing thus breaking the Single Responsibility Principle
Include a comment explaining the expected behaviour of the function
Break it down if it is becoming too long or complex or doing multiple things. The writer here explain this using a beautiful example - For example, say that you want to write some code that does three things: fetch a web page, extract data from the page and print the data out to the terminal. You wouldn't write a function that extracts the data from the web page AND prints the results out to the terminal, because perhaps one day you might need to change your code to save those results to a file instead or push them to an API.
If you try to handle all three of these output cases in your function, you'd probably end up having to add another parameter to specify which output to use, and then add that argument to every place in your code that calls the function. Then what about specifying a filename for when saving the results to a file? Or the URL and access credentials for pushing the results to an API? You can see how breaking your code down into smaller reusable functions prevents it from becoming exponentially more complicated and keeps it flexible for reusing around your codebase.
A basic principle that has helped me taking the right decisions is to look at the code after completing. When you are in the zone, it becomes difficult to think about how the overall code is panning out. It is only after a break when you look at the code again, you can evaluate if the overall structure makes sense to you and would it be clear to others. Remember two things while reviewing your code - it is a team exercise and reading code is harder than writing. So make extra effort for your team - it helps.
Ending with Linus’s Law - “Given enough eyeballs, all bugs are shallow”