Aesthetic Code: Six Principles

I don’t know how many people are interested in reading about programming – usually my own interest in the topic runs about as far as whatever particular problem I’m trying to solve at the moment. While I usually write about art and aesthetics and meaning here, I don’t think that technical work and creative work are as far diverged as many imagine them to be. Programming is in many ways an aesthetic endeavor: While a program may have to meet certain parameters to solve the problem it’s created to address, there is much room for interpretation, organization, and arrangement. Aesthetics have an impact on both the readability and the utility of code: Judicious usage of spacing and brackets, descriptive variable and method names, and intuitive placement of object properties can convey information about function and relationship and, just as clarity in prose or a carefully composed image in art, aid the formulation and communication of ideas. Having clear relationships between the different components of the program not only makes the code easier to maintain, but also makes it easier to avoid creating bugs in the first place, since many errors tend to stem from confusion about what part of the program ought to own and maintain information. What follows is a set of rough ‘rules’ that I tend to follow when programming. I’m a bit naive about the existing styles of thought in software design, so I may end up misapplying or redefining a term or two, but hopefully the ideas still carry. You obviously don’t need to follow these in your own code, but in cases where our styles diverge you may find it helpful to reflect on the reasons why your way is more useful or makes more intuitive sense to you.

1) The descriptiveness of a variable should be proportional to the longest distance from its declaration to its use. If you’re fetching the location of an instance of class Spoon and storing it in a point, then immediately using that for a calculation, just call it p: Everyone can see what it represents at a glance anyway, and a short name makes the calculation easier to read and understand. If it’s getting used much later in the method, and there are no other locations to confuse it with, call it location: It’s probably clear from context what object’s location is being represented. If it’s a property meant to be accessed from other classes, call it spoonLocation: There’s no possible room for ambiguity, no additional context needed to understand what it represents. It’s like introducing a character in a novel: If they’re introduced right as they perform some important action, it will be easier to remember who they are and what drives them – and, if they leave for a long while, the audience will probably need some sort of refresher to remind them what this character’s deal is when they eventually return.

2) An object should own any information meant to describe that object, and this information should not be duplicated. This may seem obvious and it usually is, but what is logically a property of which object, and what information is functionally dependent on other information, can sometimes turn out to be tricky. Say we have a Worker class and a Supervisor sub-class tasked with evaluating the performance of Workers: A worker may have an “enthusiasm” property, but if so it’s completely different from and potentially independent of the supervisor’s perceived “enthusiasm” from that worker – and distinct, as well, from the supervisor’s personal enthusiasm for the job they’re doing. If the supervisor has a consistent bias that always colors their perception of worker enthusiasm the same way, perhaps the worker could store their enthusiasm and the supervisor simply have a method that reads the base enthusiasm and returns their perceived enthusiasm – or, if the outcome depends on more factors, if it changes over time from when the evaluation last happened or based on how much coffee the supervisor has had, perhaps it should be a property stored by the supervisor. The answers to these questions are not always obvious: Separating the traits of similar and interrelated but distinct objects can be tricky. Just like with writing different characters in fiction, we must strive to see the world from multiple perspectives.

3) Break apart and condense logical tasks. This is, perhaps, largely a matter of interpretation: What is a “task” here? In this case, I largely mean any block of code that is interconnected enough and does a discrete enough job that it would be easy and useful to split off into a separate method – and, indeed, sometimes that’s exactly what you want to do, since just calling a method called FindTargetPointForLevelChange() or somesuch can at times make the intent of following code much clearer. Even if you decide against that, though, either because the task is too simple or its variables too interwoven with other code, it is helpful to separate it out with a couple of line breaks and possibly add a brief description to a comment above it, and try to keep it as condensed as possible within those bounds. I used to believe that cramming code together impeded readability (and it certainly can) – but as long as you take care to preserve clarity then the less space it takes up the more you can see at a glance. Think of it as creating a paragraph in the chapter that is this method.

4) Work from the outside in. Before you even start writing your class, write code which uses it – then dummy in all the methods you called in that code, then add comments on what each method is supposed to do. After that you can start implementing – and if you’re not sure how to implement a method, just write it as a bunch of comments or calls to non-existent methods that do some component of what needs to be done, then add more comments describing how those methods work and so on. Eventually, if you keep breaking it down this way, you can get it to a point where it’s relatively trivial to just convert each comment into an equivalent line of code. This approach not only simplifies tackling complex problems, but helps ensure the final interface lines up with what you, at least, find useful and intuitive. It’s like creating a gesture drawing, coming to understand the shape of the subject and how it moves before coming to the more nuanced understanding, the shading and anatomy.

5) Comments ought to be clarity’s final resort. I think comments are great and you should never hesitate to add them if you think they will increase clarity, but they should only be added after all other options for communicating the code’s functionality are exhausted. Even if in the end you decide comments are needed for clarity, it will be much easier to interpret the meaning of those comments if the code after them is already made to be as readable as possible. Kind of like adding footnotes or subtitles, sometimes it’s necessary for adding context and meaning, but it ought never stand in for the actual readability of the text.

6) Don’t write files longer than 500 lines. This is obviously a highly personal preference of mine, but I find that 500 lines is about where my threshold is for knowing basically where everything is in a file and keeping track of each part of it in relation to another. This is also usually a threshold where I find that I’ve probably made something I can easily break out into a secondary class or struct to handle a lot of what I’m doing – for instance, I might find myself performing a lot of operations with points premised on them being the location of a Spoon, so I could just refactor them into a struct called SpoonLocation which holds the same information along with all of the methods I’ve been creating whose only purpose is to modify or interpret that location data. In all art, it’s best to avoid overwhelming your audience with too much information at once, and to try to break it up in a way that makes it easier to understand what role each part serves.

My programming experience is largely as a solo developer, so there are probably considerations to working on a team or a more regimented environment I’m not aware of, but these are the principles that have worked best for me. All of the above are just the methods I work by, and I’ve also broken each and every one of these ‘rules’ on multiple occasions – I still stand by them, though, because nearly every time I do break them I end up regretting it, or at least being mildly annoyed by it. I don’t expect anyone to copy these and reproduce my style, but the intent of writing this is to, perhaps, make you more aware of your own rules and standards and prompt you to think about why you’ve adopted the ones you have.

Leave a Reply

Your email address will not be published. Required fields are marked *