6 Coding Fundamentals for Musicians

Rick Moore
12 min readFeb 15, 2021

--

Photo by Gabe Pierce on Unsplash

Save a few anomalies, when starting down the road of learning to play a musical instrument, no one starts off a virtuoso. Learning such a skill takes patience, and attention to practice and application which, if we’re being honest, is work that is never truly “complete” throughout a musician’s career. No one performs a concerto after a week of practice. They first must dig not only into the physical aspect of playing an instrument, but also attempt to decipher the maze of theory that surrounds “music” as we know it today (or at least the “Harmonic Style of 18th Century European Musicians”, but that’s a topic for another day). Musicians typically gain knowledge of a particular instrument alongside the more fundamental concepts of music, which, later on, allow for far easier explorations into different instruments or musical styles.

Since I began studying algorithms and coding (after 20-ish years of primarily being a musician), I haven’t been able to un-see the stark similarities between the learning process of music and learning to code. There are clear fundamental concepts that are used in programming that are, albeit in various capacities, implemented in nearly all programming languages. These are the foundation, the rudiments, our scales, chords, intervals and rhythms. When learning to code, it is crucial to build up these fundamental skills and understanding, which allow a programmer to quickly pick up a new coding language or framework, just by learning a bit of syntax. Strong fundamentals are what make coders versatile, intuitive, and ultimately, valuable to teams. I’ll lay out 6 coding concepts that beginners should make sure they understand, no matter which language they are studying.

1) Errors are your friend

If there’s one glaringly obvious commonality between music and programming, it’s making mistakes. Everyone makes mistakes, and in music, unfortunately we don’t usually have direct feedback letting us know we made an error, other than our own ears or an instructor. Programming languages, however, will immediately tell you if something is wrong via an error message.

Too often, beginning programming students will write a block of code and cross their fingers as they run it. An error pops up and they get discouraged, or defeated because the code they wrote didn’t work. They jump right back into the code, frustrated, and change a bunch of random lines they think might be the problem, without really taking the time to understand the error message. Experienced coders, on the other hand, will have the opposite reaction. It’s true that coding can sometimes feel like “getting through the errors”, and that’s okay! Errors were designed to give the developer detailed information about what went wrong in their code. They typically tell you where the bug is hiding, down to the line number and sometimes even the character in that line. They tell you what kind of error occurred, and a list of actions leading up to the error. Experienced developers understand how to embrace and appreciate errors for showing them where to look next and providing insight into how to solve the problem at hand. Errors are a good thing! Hug an error today!

2) Loops and Iteration

Every coding language implements some form of looping mechanism. Loops allow us to tell the computer to run a certain line of code over and over, based on some condition. A program that could have required hundreds or thousands of lines of code, can be accomplished in a few efficient lines, that are programmed to “play again from the beginning”. This process of repeating a process in order to generate an outcome is called “iteration”.

A strong understanding of how to use loops will transfer into any coding language, and allow a developer to quickly write complex logic, just by learning the syntax of the language. One of the most common types of loops is usually called a “for” loop, which takes a list of items, and applies a bit of code to each element in that list. This can be thought of as “iterating” over a list. Though the way you will write a for loop will vary from language to language, the concept is fundamental to coding in general. Here is an example of the same for loop in 4 different coding languages. I want my code to take a list of the numbers 1 through 5, and print out each number, one by one, to the console:

Ruby:

array = [1, 2, 3, 4, 5]
array.each do |num|
puts num
end

Python:

array = [1, 2, 3, 4, 5]
for x in fruits:
print(x)

JavaScript:

const array = [1, 2, 3, 4, 5] 
for (num of array) {
console.log(num)
}

C#:

int[] array = {1, 2, 3, 4, 5};
foreach (int i in array)
{
Console.WriteLine(i);
}

See the similarities? Whether I’m sailing through a sax solo, or accompanying a singer on piano, I may have to adjust what my hands do, but the way I’m thinking about the music is generally the same. There are things I can do with the sax that I can’t do with the piano, and vice versa, but the foundation is largely identical. Master the concepts of basic loops and iterations and you’ll already have a basic understanding of a fundamental part of nearly every programming language, and be able to start exploiting that languages unique handling of loops.

3) If/else if/else Conditionals (Branching)

According to Wired, the typical modern automobile contains upwards of 100 million lines of code! Now a lot of that isn’t necessarily related to operating the vehicle. It’s contained in the GPS, the entertainment system, or other non-essential systems, however most people don’t realize that there are if/else conditions between your foot on the brake pedal, and the physical brakes that actually stop your car.

An “if” statement is a necessary component of our coding fundamentals, and the concept is quite simple. They allow us to make comparisons between pieces of information, and run certain code based on that comparison. The word “if” makes the code read like English, and this keyword is pretty standard among most programming languages. It’s usually accompanied by some form of “else if” and “else”. We can ask a question, and if the “if” question is true, the “if” code will run. If it’s false, but the “else if” question is true, then that subsequent code will run. Finally, if neither of those questions is true, then the “else” code will run. For example, we may want to code a statement like “If it is sunny, write down ‘I love the sunshine!’, but if it’s raining, write ‘I hate the rain!’. If it’s neither sunny nor raining, write ‘I wonder if it will rain?’”

Here is our if statement in a few languages:

Ruby:

if weather == "sunny"
puts "I love the sunshine!"
elsif weather == "raining"
puts "I hate the rain!"
else
puts "I wonder if it will rain?"
end

Python:

if weather == "sunny":
print("I love the sunshine!")
elif weather == "raining":
print("I hate the rain!")
else:
print("I wonder if it will rain?")

JavaScript:

if (weather === "sunny") {
console.log("I love the sunshine!")
} else if (weather === "raining") {
console.log("I hate the rain!")
} else {
console.log("I wonder if it will rain?")
}

C#:

if (weather == "sunny")
{
Console.WriteLine("I love the sunshine!")
}
else if (weather == "raining")
{
Console.WriteLine("I hate the rain!")
}
else
{
Console.WriteLine("I wonder if it will rain?")
}

If/else if/else statements form the backbone of most modern programming logic. When exploring a new programming language, this is your C Major scale. Learn it first!

4) Variables

Developers commonly think about variables as a box, with a name on it. That box can hold all kinds of things, and you can place things in or take out and use things in that box with your code. You generally can decide the names of your variables, and it’s usually conventional to make your variable names descriptive, so you or someone else looking at your code can easily identify what the variable is intended to hold.

Now the reason this is such an important fundamental concept to be familiar with is that different languages all have a slightly different method to handling variables. There are 2 variable types in modern programming languages, Python and JavaScript for example use only reference types, while others (like C#) utilize both:

a) Value Type:

Value type variables will carve out their own little home in your memory. When you call this variable, it retrieves its own data from its allocated memory position, and if you copy the value or set the variable to another variable, the new value gets placed in another location in memory, which works independently of the first.

b) Reference Type:

Reference type variables use a pointer variable to store a location, where the data of the variable is stored, but not the data itself. Assigning a reference variable to another will just make a copy of the pointer reference, so they will effectively be referencing the same storage location in memory

Variables are an integral part of every programming language, and it’s important to have a base understanding of how different variables reference their data, as this plays a big part in the variable assignments in your code working as expected. Some languages are also hard to define in these concrete terms, but the important part is that you are clear in how the language you are using handles variables. Are the variables like physical objects holding a value, or are they pointing to an object somewhere else? See the link in the references for a list of variable types in different languages. Ruby can use a test to see for ourselves:

a = 10
=> 10
a.object_id
=> 21
b = a
=> 10
b.object_id
=> 21
a = 15
=> 15

As you can see, Ruby is interesting because it is made up entirely of objects. We can see the object that a variable is referencing with the .object_id method:

First, we assigned the variable a to the value of 10. It is referencing a number object with the id of 21.

Then, we assigned the variable b to the value of a. b is now also referencing that same number object, with the id of 21.

Finally, we re-assigned a to the value of 15. What is the value now of b? What do you think the object id of these 2 variables would be now?

a
=> 15
a.object_id
=> 31
b
=> 10
b.object_id
=> 21

The variable b remains equal to 10. So this means that the variables are not deeply linked to each other, and are referencing separate objects in memory. So these variables are Reference Types.

Tests like this one can reveal the nature of the variables we are using in a particular language.

5) Functions and Abstraction

Musical instruments are complex mechanical tools, that often require lots of interconnected parts that the player often doesn’t think about. By pressing one key on a piano, a domino effect begins, ultimately striking a string with a hammer and creating the familiar sound of the note. The musician isn’t thinking about the hammer, or the string, or all the complex moving parts in between, but only about the note they wanted to produce. This concept in coding, of hiding the inner mechanics of a command, is called “abstraction”, and we use functions to achieve different levels of this fundamental concept.

In coding, we write “functions” that consist of blocks of code that do a job in our code, and provide functionality that can be reused throughout our code base. These functions make our code cleaner, more organized and readable, and can utilize several lines of code with much simpler commands.

Nearly all programming languages have a system of writing blocks of code that be reused and called upon when needed. Some languages also utilize “Object Oriented Programming”, allowing the developer to define “classes” and create objects that have their own functions. These classes and functions are defined and stored in memory. Let’s define our piano. I’ll use Ruby for this example:

class Piano
def press_key(note_id, velocity)
puts "Playing note #{note_id} at volume #{velocity}"
hammer = Hammer.find_by_note(note_id)
hammer.strike(velocity)
end
end
class Hammer
attr_accessor :note
@@all = [] def self.all
@@all
end
def initialize(note)
@note = note
self.class.all << self
end
def strike(velocity)
puts "hammer striking the string at strength #{velocity}"
end
def self.find_by_note(note_id)
self.all.find{|hammer| hammer.note == note_id}
end
end
piano = Piano.new
Hammer.new(1)
Hammer.new(2)
Hammer.new(3)
piano.press_key(1, 100)

This may look like a lot of code, but that’s the point. The user wants to pass only certain information along for the function to use. In this case, the only information we need from the player is “What note was pressed?” and “How hard did you press it?”. We can pass along this information using “arguments” in a function. Arguments follow a function call and are typically inside parentheses and, separated by commas. In our piano class, we first define our function “press_key”. When this function is called (when that key is pressed), it will accept the information via its arguments. The last line is what starts the whole domino effect, everything before that just sets it up. With piano.press_key(1, 100), we are telling the piano to find a hammer for note 1, which in turn tells the hammer to strike its string the with a velocity of 100. We have “abstracted” away the player needing to know about the string or the hammer or any of their functionality. They only need to press a key, and the rest is taken care of by the code.

Different programming languages have unique ways of handling abstraction and functions syntactically, so it’s important to explore these early on when learning a new language.

Ruby:

def my_function(argument)
puts "Hello from a #{argument} function!"
end
my_function("Ruby")=> "Hello from a Ruby function!"

Python:

def my_function(argument):
print(f'Hello from a {argument} function!')
my_function('Python')=> "Hello from a Python function!"

JavaScript:

function myFunction(argument) {
console.log(`Hello from a ${argument} function`);
}
myFunction("JavaScript")=> "Hello from a Javascript function!"

6) Objects and Databases

Almost every application needs to store data, though different languages and frameworks have their unique approach to accomplishing this task. There are 2 types of databases that are popular today, SQL and non SQL. Understanding the fundamentals of communicating with databases is necessary for any developer.

SQL or “Structured Query Language” databases can generally be though of as a large spreadsheet with several tables that are related in various ways. There is a specific syntax in the commands used to interact with a SQL database, which are very similar across the different tools used to integrate database functionality into our programs.

NoSQL databases have their advantages and disadvantages when compared to their SQL counterparts. They allow for more flexibility as data is stored in documents, with key value pairs and a fluid data structure. SQL databases can be more rigid about the data they store and how you can interact with it. NoSQL can be much easier for developers to learn, and is optimized for fast queries. Adding new features can be quicker and easier due to the flexibility of the schemas. Since Non-SQL is optimized for queries, its databases are generally larger, and are not ideal in every case. SQL databases are typically better for analyzing relationships between your data sets. It’s important to pick a type of database that serves the needs of your particular program.

In conclusion, there’s a lot of opinions floating around about what is the best programming language or framework to learn for beginners. I also have my thoughts on which instruments are the best to start out with for beginning musicians, but the similarity remains: As long as the student is focused on understanding the fundamentals of the craft through their medium of choice, then the knowledge remains useful. These fundamentals will be necessary no matter what language you are working in, and will still apply later on when you explore different languages. It’s important to keep practicing, and building understanding towards a mastery of the craft.

SOURCES:

--

--

Rick Moore

Software Engineer at Thrive TRM. Specializing in Ruby on Rails, React/Redux and JavaScript.