+
+
+
+
+
+
+
+
+
+
+
+
🚧Blog under construction. Older collection of published works here.

What Over A Dozen Software Languages Taught Me About Software Design

2025-05-02

#all#growth

Preface

This is a personal thought piece, not an educative source on language paradigms. However, I hope it helps anyone who is new to programming and overwelmed by the plethora of software languages to choose from.

The Experiment

In 2020, I gave myself an odd little project: learn a new programming language every month for a year, plus a few bonus months because I couldn't stop. Haskell, Groovy, Go, Rust, Clojure, Ruby, Scala, Scheme, R, Julia, Elixir Smalltalk, Java, C++, C, Lisp, and Assembly.

The goal was to understand their differing language paradigms and categorizations — functional, declarative, object-oriented, procedural, imperaetive, low-level vs. high-level abstractions -- not in just syntax, but architecture design, why they were created, and how they contrast.

The Method

For some languages, I required more in-depth knowledge for my university studies. Those are Java, C, C++, and Assembly. The rest were out of curiosity, and of those, I spent the majority of my time with Lisp and Smalltalk.

For each language, I:

The Result

I don't remember every detail from that year anymore. All I have are rough notes from that time, and a repository for it containing a the project code and language info tables: https://github.com/kyleecodes/Language-of-the-Month-2020.

What I do remember are the lessons that stuck with me enough to continue shaping my approach today, 6+ years later.

What This Actually Taught Me

Syntax is the easy part. Paradigms are the actual cognitive shift. Writing Haskell or Clojure after years of imperative, OOP-centric habits forces a real shift. You stop reaching for loops and mutable state, and start reaching for recursion, immutability, and composition instead. Once that clicks, it's hard to unsee! Even back in an OOP or procedural language, you start noticing where mutable state is doing more harm than good.

Low-level languages teach you what the higher-level ones are hiding. C and Assembly strip away nearly everything. Learning memory management, pointers, how a function call actually works at the hardware level. Languages like Ruby, Go, and Python abstract away so that it's easy to forget. That contrast makes you a better engineer in the high-level languages too, because you understand what's happening underneath the abstraction instead of just trusting it blindly.

OOP should not be a requirement for learning functional. Instead, paradigms should be taught in the chronological order they were created. We don't need to become experts in every paradigm to earn a Computer Science degree. But learning multiple paradigms, even just briefly earlier in your coursework, instead of completely ignoring them, this teaches you that different paradigms solve problems in fundamentally different ways, and their impact on computer hardware as Moore's Law progressed.

Every language is an opinion about how software should be structured. Go's simplicity and built-in concurrency model is an opinion. Rust's ownership model is an opinion, enforced at compile time instead of left to convention. Lisp's "code is data" philosophy is an opinion. Ruby's obsession with developer happiness and readability is an opinion. None of these are neutral design choices. They're all making a bet about what tradeoffs matter most, and consider the specific solutions to the problems they were trying to solve.

Object-oriented design is a tool, not a default. But defaulting to it is still good practice. Coming from academic C++ and Java, OOP can feel like the "normal" way to write software. Spending real time in Smalltalk, one of the languages that actually originated many OOP concepts, versus a functional language like Lisp back-to-back, makes it obvious that OOP is a deliberate set of tradeoffs (encapsulation, inheritance, message-passing), not a "natural state of code", because that doesn't exist. However, defaulting to the most widely used paradigm is still a great programming practice. An issue I have with Python and JavaScript for example, is there are endless solutions to resolve the same problem. A language with a more "pure" architecture, and more restrictive dev tools, will limit those options to predefined solutions. This makes problem-solving easier, but developing an arsenal of good programming habits can be achieved regardless of the language or paradigm you choose.

Final Thoughts

Looking back, the goal transformed from fulfilling curiousity to teaching me to stop thinking about "how to code" as one thing, and start seeing it as a landscape of different, deliberate answers to the same underlying questions.

Learn More: