The easiest code to read, is the code that is not there.
If two programs are exactly the same; they look the same; they behave the same; they take the same input and provide the same output, but one was written with 1000 lines of code and the other with 10,000 lines of code. The code of the 1000 line program is going to be easier to read, easier to maintain and easier to extend.
If there were no other advantages, simply eliminating lines of code, in and of itself, would be advantageous, because it reduces the amount of complexity and the shear mass of instructions that a developer must deal with in order to work with a program.
But, once one becomes zealous in their quest to eliminate lines from the code base; once each line added becomes pain and each line trimmed becomes joy, the developer will start to notice other advantages.
Suddenly, there is a natural incentive to write for code reuse; copy and paste becomes quite unpalatable. Code starts to naturally get pushed into objects that encapsulate elegant constructs of data and function. Generally, smaller amounts of code translates into more performant code. Code written for reuse, gets run more times, from more directions; it therefore becomes harder for bugs to hide. And of course there is less code to search through when looking for those bugs. And when a bug does get fixed, it gets fixed everywhere it is called (which doesn’t happen when code is copy and pasted).
The side effects of always trying to write concise code, perhaps become a larger asset than the efficient code itself.
Whether a class, API or an app itself, design the interface first, implement it second.
It is always tempting to start with the beginning. It is much better to start with the ending.
When approaching a new problem, it is easy to make the first question, ‘How am I going to solve this problem?’, but the actual first question should be ‘Once I have solved this problem, how do I want to use it?’
If one is adding a brand new API to their app, for example, a persistence layer, it makes sense to start with the technical problem at hand. Perhaps the first question that comes to mind is ‘How am I going to store this data?’ And once one figures out how to store it, they might next ask in what form will it be saved? And then, how will the API access and perhaps cache the data? And then how will the API structure the data internally? And then finally the question of how the ‘end user’, i.e., the programmer using the API, will interact with the API comes to mind.
But when an API comes about in this manner, that last step, the programmers interface, suffers. It suffers because the interface wasn’t designed for the programmer, but instead was designed for the API. Instead the first question should not have been ‘how do I solve this problem’; but rather, ‘if this problem were solved, how would I want to interact with it?’
Once one figures out what an ideal interface looks like they, then and only then, can start to figure out how to implement that interface. In this way, whether creating a class, an API or an app itself, the end product is much improved.
One visceral example of this is the TV remote. The average TV remote is designed for the TV and as a result it is a cacophony of buttons, most of which only the engineers who made the TV have any idea what they do. On the other hand an Apple TV remote was designed for the end user and contrast between the two remotes is quite striking.
Where ever you are in the code, all the relevant code should be there.
One (of many) of the benefits of object oriented development is the tendency of a class to collect all of the code relevant to a particular topic in a single location. When working on a particular area of a program it is great to have everything you need at a single point in that code base; to have everything at your finger tips.
The alternative, of constantly flipping back and forth between many different areas of the code base trying to keep all of the various interrelated dependencies in ones head, all the while not knowing what other code depends on what, tends to eat time and breed bugs. And although, shifting from one file to another is a particularly expensive operations, scrolling too can cause similar issues. The, certainly not 100% achievable, ideal, is to be able to see all the relevant code on ones screen at one time. There are a handful of little techniques that can maximize the chances of achieving this ideal.
One fairly straightforward way, is to be conservative with vertical spacing. White space is great for signaling phrases and sentences in the code base, but it also reduces the amount of code shown on the screen.
Also, at times adding newlines to a method call itself might increase readability, but often times it reduces it by messing up tabbing and signaling undue import to a particular call, while wasting much vertical space in the code file itself.
Another area where improvement can be had is in the usage of constants. There are religious edicts passed down from old, that say that numbers should not appear in the code. Some developers take this to an extreme and create a constant for each and every number. The reality is this is generally a major blow to the readability and usability of the code base, particularly when a constant is only used once.
For example, if control A and control B have some desired space between one another. If a developer goes to the code that determines the spacing and instead of just seeing a value, they see a constant, they will need to either scroll up to the constant or move to the file containing the constant to see what it is or to change it rather than just being able to see and manipulate everything there.
And if they do manipulate it, they will need to make sure that all other code that uses the variable also wants the manipulation. Many times that may be the desired behavior, but when code becomes overzealous in using constants, it will often not be.
If a developer is dealing with two or more constants that may be in different locations that perhaps they want to compare or relate to one another in some way, the problem greatly increases in difficulty. Beyond that, how does one name all these constants in a way that accurately describe what they represent equal to or better than seeing exactly how they are being used?
Aspire to memorize the code base, write code that makes it easier to do so.
Closely related to the above guideline, one should aspire to memorize their code base. If one has all the code in their head, if they know where any given function is located and what it does, extending and maintaining the project becomes substantially easier.
Of course memorizing 1000s of lines of code is impossible. But, there are a number of things one can do to make the task easier. For example, if one were to memorize the following 6 ten digit numbers:
It would quickly become apparent that some of these numbers are easier to remember than others and for various reasons; some relate to things a person may already know, some are algorithmic with varying degrees of complexity.
One of the goals of coding should be to write the code in a way that maximizes ones chance of keeping it in their head. As code becomes more algorithmic, more predictable, it becomes increasingly easy to keep it in one’s head.
One area where this can be important is naming. For example, perhaps one has a view with a image on it; here are some potential names:
let empPortraitView: UIImageView let pictureOfEmployeeImageView: UIImageView let employeesPicImageView: UIImageView let employee: UIImageView let imageView: UIImageView
There a number of factors to consider when naming this object and one of them is memorableness. One thing that instantly reduces the ability to remember something is to abbreviate it. There are any number of ways to abbreviate a word and once an abbreviation is chosen one has created something that needs to be remembered.
Another temptation is to over describe a variable. Long variable names may make it easier to “self document” a variable, but they can reduce their ability to be remembered and can also reduce the readability of code. With any trivial calculation using overly long variable names can greatly reduce the readability of an equation. It can also make calling methods with parameters overly verbose, hiding what is actually happening.
Even if one choses a relatively concise non abbreviated name that contains an adjective, they are adding something that needs to be remembered. The most memorable way to handle this particular situation is simply call something what it is: imageView. There is no need for adjectives in the name until there are two of something. If a view has one UILabel, call it label. If it has one UIImageView, call it imageView.
There are a number of other ways one can increase the memorability of their code:
- Alphabetize imports and other areas of the code base that contain long lists of something.
- Keep the order of code in a file consistent. Group methods for extensions and interfaces; the order in one file should match the order in all files.
- Keep the order of properties the same wherever they are located. I.e., the order properties are defined, should be the same order they are translated to and from JSON and the same order getters and setters are defined, etc.
- Match the order of UI elements in the code to the order of the UI element on the screen.
- Properties themselves should be organized in a logical order; primary key at top, foreign keys next; data next and utility properties last, for example. Similar properties should be grouped together; new properties should go where they belong not just be tacked to the bottom or randomly placed.
- Static methods should be placed where they logically belong; where one would most expect them to be.
- Names should be chosen that match the language of use. For example, if your designer calls a grey color, ‘CCCCCC’, then name your color ‘CCCCCC’ instead of grey8. Calling it grey8 will necessitate a trip to the color definition file each and every time a developer uses the color until they have unnecessarily placed the naming map in their head.
- In some cases, a concise distinct name is preferable to a long winded name. For example, if you have an object that contains a list of all drugs available, you could call it the FullDrugListRepository, or you could call it Apothecary. One of those is substantially more memorable than the other.
Always be intentional about the trade offs you are making.
Software development is a constant balancing act. One is constantly weighting the relative merits of a number of different factors. It is important to be cognizant of this process and to make thoughtful decisions about which to prioritize when they come into conflict with one another. Here is a partial list of these considerations:
- form and function of the app
- readability of the code
- reusability of the code
- speed to develop
- performance of the code
- other app footprints such as memory, storage, bandwidth and battery usage
The above list is loosely order by relative importance, however, everything really needs to be decided on a case by case basis. There really, is never case where any one of these items can’t trump another.
For example, the form and function of the app is obviously of utmost importance. But, perhaps one has a very cool animation in mind that would slightly improve the form of the app, but will take two weeks to implement. In that case, perhaps ‘speed to develop’ may override ‘form and function’.
Harping back to my point about concise code; while there are often times when these factors come into conflict with one another, with few exceptions writing concise code almost always enhances all of the above considerations.
On a basic level concise code increases readability since there is less code to understand. But, since it pushes one to avoid copy and paste and increase code reuse where possible, that increases code reusability which increases code readability.
Typing less code can boost speed to develop; however writing reusable code usually takes quite a bit longer than the alternative initially. But, this initial investment can pay huge development speed dividends eventually.
Concise code often times will be efficient code. There are of course counterexamples where particularly tight loops require clever solutions that increase the code and decrease its readability, but these are situations are fairly rare and fairly confined in a code base.
And of course all of this then allows for more iterations to be tried, more malleable, less buggy code; which all points to better form and function of the app itself. Concise code is very close to being a programming magic bullet.
Certainly, performant code is desirable. However, occasionally performant code may take longer to develop or may reduce the readability of the code. And in many cases, the difference in performance will not be noticeable to the user. So, it may be best to deprioritize performance until it becomes a problem. Don’t solve performance problems you don’t have, i.e, avoid premature optimization. This leads us to another guideline:
Don’t solve problems you don’t have.
Perhaps ‘don’t solve performance problems you don’t have’ can be further generalized to ‘don’t solve problems you don’t have’. Certainly, this concept can be extended to any of the footprint aspects such as memory usage, storage, bandwidth and battery usage. But, is also an important concept in any area of app development. Is offline mode really needed? Is multi-device mode essential? Is a local persistence layer really required?
In most cases it is probably optimal to let necessity drive development choices. Which, isn’t to say one must be entirely reactionary in developing, but often times lazy instantiation really is a good idea.
If you have to explain your code, simplify it instead.
Another edict passed on from old is the importance of commenting your code. This really is quite important when writing machine language or even assembly. But, since we are probably not, perhaps its time for a little blasphemy.
One of a programmer’s jobs is to understand the language they are using. If they understand the language and see the code in front of them why is there a need to add comments? Comments add noise to a code file, comments can get stale, comments reduce the amount of code visible at one time. But, most importantly, comments excuse poor, overly complicated or convoluted code. If your code requires comments to explain, perhaps its time to re-write it instead.
A much better tool for creating readable code than commenting is elegant naming and code structure. A class’s name should make it apparent what it is; a method name should make it apparent what it does. The implementation of the method should be clear and concise.
Don’t include code you don’t understand
This may (hopefully) seem like a pretty obvious guideline, but certainly at times when deadlines loom and a code snippet from StackOverflow looks suspiciously promising, it can be tempting to plop it into place. But, the chances of this creating problems, bugs, unmaintainable code down the line is high. Ultimately, this will steal time not save it.
Taking the time to understand the code, or better yet, taking the time to rewrite the code in an understandable way will be hugely beneficial later.
The zeroth rule of coding, don’t be afraid to break rules.
Religion, conventional wisdom and fad have no place in software development; especially for competent developers. Of course the demand for software developers way outstrips the supply, so perhaps this doesn’t pertain to everyone. But, for good developers, it is best to consider each situation individually.
‘Don’t ever use goto!’ If there was never a case to use goto in C, than why did they add it to the language? No programming rule is right in 100% of cases and certainly none of the guidelines presented here is right in 100% of cases.
Never stop thinking, never stop trying to improve upon what currently exists.