“Conscientiousness is the personality trait that is defined as being thorough, careful, or vigilant; it implies a desire to do a task well. Conscientious people are efficient and organized as opposed to easy-going and careless.” - Wikipedia
I’ve worked with many software developers who would rate themselves high on the scale of conscientiousness. And that’s a good thing, as codifying best practices and gaining order from chaos is what keeps us from driving ourselves or our co-workers mad. Some developers even consider themselves ‘engineers', minus the certifications or legal liability. I’d guess that is because it implies a sense of professionalism and discipline.
This mindset extends beyond writing organized, error-free code. We often seek predictability in our development environments and automated deployments. We know how to clean a dataset or properly encode media files. But as orderly as we might be, our software is used in the real-world by people who may not be as analytical or thorough.
Sometimes it can be difficult for us to understand where they are coming from, or even that they exist. This is the lesser-known side of the Dunning Kruger effect:
“participants who found tasks to be relatively easy erroneously assumed, to some extent, that the tasks must also be easy for others.”
Intentionally or not, it is easy to forget that other people use our software differently than us. And that makes it easy to write their mistakes off as their fault. Have you ever used the phrase “garbage in, garbage out”? I have plenty of times, but I also recognize it as passing the blame, not working toward a solution. Willful ignorance will not make the problem go away, and software development is complicated enough as it is.
To quote Donald Rumsfeld’s now-famous epistemology lesson:
“… there are known knowns; there are things that we know that we know. We also know there are known unknowns; that is to say we know there are some things we do not know. But there are also unknown unknowns, the ones we don't know we don't know.”
But there’s a fourth category that he is ignoring, and that is the unknown knowns. As Slavoj Žižek put it:
“… the main dangers are in the "unknown knowns," the disavowed beliefs, suppositions and obscene practices we pretend not to know about ..."
We know users, systems, and other developers will be messy. We must work hard to overcome our bias and keep that in mind, else we turn that fact into an “unknown known”. We must expect the worst, and do our best to sort it out. We can’t turn a blind eye to the real world, even if it is messier than we’d ever let our local development environments to be.
So what does this have to do with Python? When working with web frameworks or APIs, there are many questions you can ask throughout the planning and development process.
Are you accounting for all possible inputs when processing form data? Can your form handle unicode characters? High-level frameworks like Django provide convenient methods to clean inputs, but what about when data is imported directly into the database by an administrator? Are your field requirements enforced at the database level?
If users upload images that are displayed on a page, what size or format is required? Is that requirement enforced in a form widget, or is it re-checked by the server code? What happens when someone bulk uploads media to the site and bypasses the form? Or what if the design is later changed and the existing images are the wrong size for the new design? Should the images ever be upscaled or cropped? There is probably no way to make tiny images look good, but I bet there are decisions you can make that would make them look less bad.
Do you make assumptions about your data when rendering it in a template? Are exceptions thrown if an integer is 0, or a date is malformed? You probably want to let your template rendering decide how to display the results of an exception. For instance, the results of a divide-by-zero error could be displayed as ‘n/a’ or ‘0’. But many template rendering languages like Django’s do not provide try/except controls, so identifying where or how those exceptions occur and catching them in the view or model layer will allow you to provide a more meaningful response than a generic 500 error page.
All of these examples could be stated in a more generic way: are your unit tests checking for values that might not even be possible to propagate through integration tests? Are you making sure that bad data is accounted for at every level, instead of assuming it was caught by our relatively-clean test environments?
I’d guess for most developers, these questions are already integrated into our standard development process. We recognize that “it works fine locally” is not a valid way to close a ticket. But if you are aiming for code that is as error-free as possible, you must put yourself in the mindset of those who are different than you. For some developers, that might be one of the hardest parts of the job.comments powered by Disqus