Ode to Ruby — a moment of gratitude for a very high-level language

Reflections from a new developer as I switch from programming in C to Ruby

This past summer, I took Harvard’s Introduction to Computer Science course (free on Coursera) which was an awesome intro to programming and some CS theory. We started the course by learning the C language — still technically a high-level language, though it requires a lot more ‘work’ from the developer than Python or JavaScript which I had been previously working with. A solution took two or three or more times as many lines of code in C, and I couldn’t even append an item to array. An outrage!

Fast forward a few months to Flatiron School’s software engineering boot camp. Time for a new language — this time, Ruby. I was amazed at the simplicity and readability. Everything seemed so easy. Need to sort an array? .sort. Need to filter for unique values? .uniq. It makes so much sense. I can even append items to an array again without dynamically allocating memory!

Delving into the C language has helped me truly appreciate the usability of very high-level languages. With this post I will focus on two languages that I’ve personally worked with — Ruby and C — highlighting some of the differences I’ve encountered and why my life has become a lot easier since beginning with Ruby.

Low-level vs. High-level languages

High-level languages are the English-resembling coding languages we typically think of when we think of programming — Ruby, Python, Java, C++, etc. Low-level languages include machine code (like binary) and assembly language, which aren’t very readable to humans but are readable to computers. Tools called interpreters and compilers (different, but they serve similar purposes) effectively translate English-like code into 0s and 1s which can be understood by a computer.

Within the high-level category, there is a wide spectrum as to just how high-level a language is. The C language can be thought of as a “middle-level” language since it’s closer to machine code than a language like Ruby, which can be thought of as a very high-level language as it includes many more abstractions to make it easier to use. Languages are built to be used for different purposes, of course, and offer different innate functionalities to reflect that.

Object-Oriented vs. Procedural Programming

Ruby is an object-oriented general purpose language. This means that data and behaviors are encapsulated into “objects” which are able to relate with other objects according to their blueprint or “class”. Object-oriented program simulates the “real world” in the sense that almost everything can be thought of as an object, and we can make these objects interact with other objects through methods defined in our code. Following the four pillars of object orientation — encapsulation, abstraction, inheritance and polymorphism — our code becomes powerful and dynamic.

C is a procedural language, meaning that its programs are broken down into functions, rather than objects. Procedural programming takes a top-down approach, so programs in C follow a set of instructions step-by-step. As a beginner, I found it easier to wrap my mind around procedural programming as it’s quite linear and sensical. However, procedural programming more limited in its ability to reuse code and can be inefficient if similar code has to be written repeatedly.

Data Typing

When using C, you must declare a variable with a specific data type (int, float, char) prior to using it. Ruby, on the other hand, does not expect types to be explicitly stated. Data types in Ruby are implemented as classes, so in order to get the type of a variable, you would call “.class”.

C uses what’s called ‘static typing’ whereas Ruby uses ‘dynamic typing’. The difference is that in C, the code entire code is screened for type errors during compilation. If there are any variables that are not declared or are incorrectly declared, the program will not compile. Ruby and other languages that use dynamic typing instead check variable types during runtime. A program with type errors will compile and run, but will throw an error during runtime when the error is encountered.

Memory & Garbage Collection

This was one of the more perplexing parts of writing code in C. Ruby and other modern high-level programming languages come equipped with what’s known as “garbage collection.” This is essentially the mechanism of allocating and freeing memory to be used by objects. The developer is free to develop, and Ruby itself is able to “clean up” or free space from objects that are no longer being used. In C, however, the developer herself must tell the program the amount of space to allocate (for example, an array of X length), and once that space is no longer needed, it must be manually freed by calling free().

Tbh, looking back at my C code after working with Ruby brought some bad flashbacks. I have a thorough appreciation for the gifts of these very high-level languages, especially as a new developer. However, there’s obviously a reason that people still elect to use lower (but still high-level) languages like C. For one thing, being a “middle-level” language means that C can be used for both low-level programming (like scripting OS kernels) as well as programming high-level applications. It’s quite efficient at performing algorithms, therefore is useful for calculation-heavy programming. It’s also formed the basis for a lot of modern programming languages.

So while I appreciate the power and versatility that the C language offers…for now, I’m going to enjoy using something as clean and simple as Ruby!

Resources/Learn more:






Developer in New York, NY.