When looking for quick fixes to a coding issue, we find a glut of online resources on effective programming practices, code commenting, or documentation. However, for many of us, it is hard to contextualize the information from these articles into useful solutions for our own work. This is a drawback of how programming is taught in schools, with a tremendous focus on imparting the technical knowledge required to solve closed-ended problems.

In this article, we would like to teach a coding mindset that complements technical expertise by helping to contextualize generic best practices to complex real-world problems.

Contents
  1. Introduction: What is an effective coding mindset?
  2. Write code that solves a specific problem
  3. Write code that is easy to read and understand
    1. Coding convention
    2. Comments and documentation
  4. Write code that is maintainable and extendable
    1. Reduce code duplication, enhance code reuse
    2. Modularize the organization of code and data
  5. Conclusion

Contrary to folk wisdom, the coding mindset is not like the taste of fine wine; it is not acquired only with age. It can be systematically taught to novices, too. At the same time, coders of any experience level benefit from practicing the mindset. The coding mindset helps us to apply the technical expertise we developed in school while keeping our high-level goals as programmers in sight, resulting in programming decisions that account for real-world complexities. By keeping in mind the demands of projects with multiple interacting codebases and collaborators who need to use and adapt our code, we can push ourselves to make coding decisions that prioritize clarity, modularity, and reusability.

The three high-level goals of a programmer are to write code that…

  1. Solves a specific problem
  2. Is easy to read and understand
  3. Is maintainable and extendable

The typical decision-making process in programming is informed by both technical expertise and the coding mindset. Technical expertise alone falls short when we need to choose the right solution among several possible approaches to solve a problem. The coding mindset, on the other hand, helps us pick a solution that best fulfills the sometimes contradictory high-level goals. For example, you might be tempted to use Fancy Algorithm X to solve Problem Y, but then you realize that X would run into issues with scalability when you account for Real World Situation Z. So you decide to take a step back and consider your high-level goals, and realize that it’s most important in this case to prioritize extendability. This leads you to instead choose Less-Fancy Algorithm W in support of your goals. Your collaborators are happy. You win Real-World Programmer of the Year!

An effective coding mindset begins with asking yourself if the three high-level goals as a programmer are being satisfied. If you cannot satisfy all three at once, the mindset reminds you to ask which one or two goals are most important for you and your collaborators. With practice, it will become second nature to check yourself using these questions.

The mindset is very applied in nature and can be reinforced with examples. Below, we expand on each high-level goal and provide illustrations of how an effective coding mindset can be applied.

1. Write code that solves a specific problem

Avoid trying to write a single piece of code that can solve a wide range of problems. This might sound counterintuitive, so let’s consider an example: have you ever seen a handyman use a Swiss Army knife to fix your furniture, instead of using a dedicated tool selected from a combination toolset? We can extend the same reasoning to programming: start by writing code that solves a specific problem. When the need arises, the code can be extended to solve similar problems by adding the necessary functionality. This is analogous to extending the functionality of a combination toolset by adding more tools.

Writing code that solves a specific problem makes it easier to extend the functionality in a modular way.

  • Before you start coding, articulate a clear description of the problem you wish to solve.
  • Your documentation and/or README should begin with that problem description.
  • Identify and modularize the different components in your solution that can be potentially reused to solve similar problems.

Return to Contents

2. Write code that is easy to read and understand.

Programmers often overestimate their ability to understand code written with sparse comments and inconsistent styles. Studies have shown that beyond a two-week period, an average programmer cannot tell whether an uncommented and anonymized piece of code was written by himself/herself or someone else – which in turn decreases the ability to understand and reuse code. This highlights the need for adopting standards that make code easier to read and understand.

2.1 Coding convention

2.1.1. Variables and functions are to code what nouns and verbs are to communication.

  • Each piece of information that needs to be stored is a variable.
    • A variable’s name should describe its contents.
  • Each set of actions we perform in the code should be contained in a function.
    • A function’s name should describe the set of actions it performs.

Just as any form of communication becomes ambiguous when we use a single noun or verb to refer to multiple objects or actions, reading code becomes difficult when variables and functions are given names that are not unique and specific.

2.1.2. Hard coding is a cardinal sin of programming and should be avoided at all costs.

Hard coding means that data or parameters are written directly in your code, rather than read in or generated from an external source. This makes code overly customized to one particular problem or configuration, and accordingly difficult to extend to a larger set of problems. To avoid hard coding, identify which of your parameters are likely to vary based on the problem or dataset you’re addressing. Set up your code to read that information in at runtime from a configuration script, command-line input, or similar.

Constants that are not specific to a problem should be defined as variables with descriptive names. For example…

  • Your future self might be confused if they see “if (answer = 3.841)“. Is 3.841 a parameter you made up?
  • Consider instead: “chi_square_05 = 3.841“. Now it’s clearer that you’re testing for statistical significance, and the number has mathematical relevance.

2.1.3. Use a consistent coding style that helps leverage our visual ability to recognize spatial patterns and order.

Click here to read our CommKit article on developing and using effective coding style.

2.2 Comments and documentation

Comments and documentation serve unique purposes and should together provide the end-user and developer with all the information they need to easily use, maintain, and develop the code. Comments explain non-obvious programming choices, while documentation tells the reader what the code does.

2.2.1. Comments in the source code explain programming choices that are not obvious.

The objective is to leave helpful trail markers for a future version of yourself or a fellow code developer who wants to maintain and extend the code.

Avoid cluttering code with obvious comments such as “stores a value of 10.” Instead, explain non-obvious rationale. For example…

  • You have chosen bubble sort to sort a presorted list to which an element is appended during each iteration. However, another code developer might wonder why a more asymptotically optimal algorithm such as quicksort was not employed.
  • Solution: Leave a comment explaining that bubble sort was preferable in this particular scenario because it is the fastest algorithm when working with lists that are almost-sorted.

2.2.2. Effective code documentation should clearly outline…

  1. The problem the code sets out to solve
  2. The methodology used to solve the problem
  3. How to find relevant background, such as the algorithm, validation studies, or benchmarking studies
  4. Where to look in the code for parts like reading the input, algorithm to solve the problem, writing the output, etc.

Return to Contents

3. Write code that is maintainable and extendable

3.1 Reduce code duplication, enhance code reuse.

This is the mantra of object-oriented programming that helped the computer science industry take off in the ’90s, following a decade of only nominal progress with structured programming languages that have a lot of code duplication. Code duplication greatly increases the risk of code corruption when changes are not propagated to all instances of the duplicated code.

To create reusable code, develop functions that perform unique tasks (remember: functions are action verbs). It is also good practice to refactor code at regular intervals to maintain a lean codebase, further enhancing code reuse. As a general rule, when you can’t find a simple descriptive name (action verb) for a function, that is a call for refactoring that function.

3.2 Modularize the organization of code and data.

Modularizing code and functionality to avoid duplication provides a clear structure, making it easier to navigate and reuse the code. The chances of data corruption can be largely reduced by keeping data spatially and temporally local. In other words, aim for the shortest possible distance between a variable’s first appearance in the code, and the location where it’s actually used. Improved locality makes it easy to find and fix bugs both for a future version of you and for others.

  • Consciously limit the active scope of each variable to use its contents at a place and time in the code that is as close as possible to its generation.
  • Move all code inputs and parameters to a configuration file that is loaded at startup to allow for dynamic execution of the code and prevent hard coding.
  • To help modularize your thoughts about testing and debugging the code, you can…
    • Use code assertions to ensure physical behavior at all times. Assertions allow us to halt the execution of the code when an unexpected input or state is detected.
      • For example, we can use code assertions to make sure that attributes such as the mass of an object, count of objects, etc. cannot be negative.
    • Leverage automated testing to provide ongoing quality assurance of code. This can be extremely helpful in large projects.
    • Translate each semantic bug that is detected into a unit test, to organically grow the database of automated tests.

Return to Contents

Conclusion

The practical strategies introduced in this article will help you exercise and reinforce a coding mindset that supports your high-level goals as a programmer. In summary…

  • Always remember the specific problem you are solving.
  • Make naming and structural choices that reflect that specific problem.
  • Avoid code duplication and improve the spatial and temporal locality of data.

To learn more about how to put your coding mindset into practice, explore our other articles about best practices for coders:

Return to Contents