published 2025-01-19

My Coding Journey

In this article, I reflect on many of the programming languages that I have learned and that have influenced me. I'll describe my motivation to learn them, what I liked and disliked about them, and how they still affect the way I program today. This is not a complete list of all languages I ever used. I excluded those that had minimal impact on my development.

Different languages solve the same problems in different ways. By learning several different approaches, you can help broaden your thinking and avoid getting stuck in a rut.

The Pragmatic Programmer

If you are unsure about whether you should learn Scala or Kotlin, or are completely open for a new paradigm, I hope there is some inspiration you can draw from this write-up.

Getting started

I grew up surrounded by computers, with an Amiga 500 as my gateway drug to PC gaming. During my childhood, I spent many hours in front of the screen. As a teen, curiosity grew beyond just playing games and tinkering around. I wanted to learn what is required to create my own programs, and to understand how it all worked behind the screen.

BASIC

The first programming language I learned was BASIC. I stumbled upon a book about BASIC in my dad's library, and it was written simple enough that I could follow and understand the foundations. QBasic also came with MS-DOS, making it easily accessible on most computers I had access to at the time. A bit later I started building applications in Visual Basic, which was also very beginner friendly, allowing you to create GUI applications in a WYSIWYG editor by just dragging & dropping elements and hooking up code to interactions without requiring deeper knowledge.

It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration.

~Edsger W. Dijkstra

It is hard for me to judge if BASIC had a negative impact on my future ability to learn programming. I wouldn't recommend learning it whatsoever.

It is easy to get started with it, but you will reach the boundaries of what is possible in a sane way quickly, forcing you to switch to a more expressive language to make real progress. I don't think it's worth learning BASIC anymore. Instead, I would recommend starting the journey with Python or even Elixir.

Lessons learned

  • How to structure data with global variables & arrays
  • Structuring code using GOTO and subroutines
  • Drawing fancy graphics in text mode with Code page 437

PHP

As the internet slowly became popular (roughly half of the German households had internet access in 2003), me and some of my friends started building websites. Of course I aimed for more than just static web pages, so HTML didn't cut it. I did not like JavaScript (which hasn't changed at all), so I learned PHP. This is when I inadvertently became a backend developer. It started with some basic form handling, but SQL quickly followed. I even wrote my own CMS, not knowing what a CMS was at that time.

PHP had an edge by allowing you to expose programs to the world by just dropping files on a server over FTP, without the need to compile anything. This made it easily accessible to someone who didn't even know what a compiler was.

On the downside, those scripts got out of hand quickly, so I was longing for something more structured in short order.

Lessons learned

  • How to use maps and functions
  • Basic knowledge about HTML & HTTP
  • Dealing with Apache & relational databases (which I still use on a daily basis, more than 20 years later)

Python

One day, a friend made me aware of Python. There was an Apache module (mod_python) which made it possible to write server side applications in the same way as with PHP. I immediately fell in love with it. Its syntax and structure was cleaner, and the standard API was much more powerful. So I rewrote all my PHP code in Python and never looked back.

Python also had an awesome free tutorial and a forum in the German language. This made it really easy to learn for someone who is not a native English speaker. Remember, this was still way before StackOverflow and YouTube, so finding help learning something that "exotic" was hard. I would still recommend learning Python to a beginner. I wouldn't call it exotic anymore though.

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.

from the Zen of Python

With its explicit self reference in method calls, learning Python suddenly made classes and objects click in my head. Before, I had struggled trying to learn Java, but never really understood it. The explicitness of Python makes it a prime language for learning programming. And it gets you very far.

From Linux tooling to machine learning, everything is glued together with Python nowadays, so it is really hard to avoid as a professional. Its biggest drawback is performance though, so unless you couple it with more efficient languages, there is a limit of what you can achieve with it.

Lessons learned

  • Code can be beautiful
  • How classes, objects & inheritance work
  • Building games using an update & render loop

University

With a solid basis, though no formal education in programming so far, I decided to study computer science.

Standard ML

The first language I had to learn in university was SML. It was my first contact with a functional language (and unfortunately also my last one in university). Most functional concepts were foreign to me, but I had no trouble understanding them. Back then, I questioned the principle that all students had to go through SML as a first language. In hindsight, I think more people should start learning programming with a functional language though.

I aced the first programming exam. This could have been a sign for me. Unfortunately, OOP was completely overhyped during my time in university (and it still is). There was little for other paradigms left. So I decided to attend lectures in so-called "Enterprise Computing" instead of spending more time on functional programming.

Lessons learned

  • How to write algorithms using recursion and tail recursion
  • Pattern matching
  • Currying

Java

My attempt to learn Java at university was successful, since I already had experience with OOP concepts from Python. I could focus on static typing (generics just made it into the language), which was completely new for me.

I rode the hype train and became a sucker for Java. When Android launched in 2008 with Java as primary language for apps, my path was cemented. It took a long time to unlearn the bad parts of OOP because I was so deeply engaged in it.

It is hard to say what was so appealing about Java to me. Probably it was a mix of simplicity, availability and the static typing, which allowed powerful tooling like autocompletion.

Lessons learned

  • Static typing
  • How to do "proper" OOP with design patterns
  • How the internals of a VM, bytecode assembly and garbage collection work

C

In 2008, a lot of things happened that would shape the future of programmers. GitHub was founded (which I still use daily today) and StackOverflow launched, which was a huge help trying to solve programming issues for many years to come.

I started with C in course focused on high performance computing. I never really warmed up to it though. It was complicated to get something done in contrast to high level languages. Also, debugging was much harder. But there was no way around it if you wanted to write code close to the hardware that performed well and didn't come with a lot of overhead, so it was a useful thing to learn at the time.

I've only used C sparingly during my career, whenever it was unavoidable. Luckily, with Rust there is an alternative now that performs just as well, but with more modern tooling and without the footguns.

Lessons learned

  • Pointers & manual memory management
  • How to make code run really fast with microoptimizations
  • Language ergonomics are more important than raw speed most of the time

Professional OOP

Starting my career as a software engineer in an agency, I was surrounded by many programmers with various backgrounds. I was exposed to lots of languages I had never heard before, more than I could learn at once. So I had to make some tough decisions where to invest my time.

Scala

My interest in functional programming was still there. I found some like-minded people and did a course on Scala. I picked it mainly because it runs on the JVM, which had some familiarity and I already understood what is going on "under the hood". Unfortunately, this was also what held back my deeper understanding of many functional principles. In my head, Scala was just another syntax for the JVM. I mapped most of the functional ideas to concepts to I already knew from Java, without really grasping the difference.

Python is an experiment in how much freedom programmers need. Too much freedom and nobody can read another's code; too little and expressiveness is endangered.

~Guido Van Rossum

At first, I was impressed by the richness of Scala features compared to other languages. Effectively, the amount of different concepts mixed in Scala made it hard for me to understand. Code was written in many different styles, so it was difficult to get attached to the language. This also showed in the fact that there were not many projects which actually used the language.

If I had to decide for a functional language on the JVM today, I would probably choose Clojure instead, being more minimalistic, concise and focused on immutability.

Lessons learned

  • Message passing & the actor model
  • Having a huge set of features can be detrimental for a programming language
  • Interoperability can limit the growth of a language

Kotlin

The evolution of Java got a lot slower, trying to stay compatible to older versions. When Kotlin came out, it introduced better type inference and null-safety, with full interoperability to existing Java projects.

I bet on it to take off in 2016 and started learning it right when it came out. This turned out to be the right decision when Google announced first-class support for Kotlin in Android a year later. Also more teams shifted their Java backends to Kotlin soon.

Kotlin was a huge leap forward in terms of programming ergonomics. Compile time null-safety was the absolute killer feature compared to Java. Immutables and data classes were a nudge in the direction of more functional style code.

I would have loved to see Kotlin Native take off quicker, however it suffers from the same issue as Scala: because libraries written in Java work really well with Kotlin, there is no need to rewrite them. Because applications can only use the Kotlin Native compiler if they are exclusively written in Kotlin, this makes it a lot harder to find appropriate libraries for a native project. So the interop was good for a quick start, but holds the language back to grow beyond its roots.

Lessons learned

  • Compile-time null-safety is a must for statically typed languages
  • Immutability makes code a lot cleaner
  • Functional chaining is great for concise, readable code

Dart

At one point in my career, I inherited a React Native app which I had to maintain. It was a huge liability, the performance was poor, and the JavaScript ecosystem made it horrible to maintain. Over time, more dependencies broke, making it painful to update at some point. When Flutter 1.0 was released, I decided to learn Dart and rewrite the app in Flutter.

I never learned a programming language as effortlessly as Dart. It had no new concepts, and its syntax was familiar. That made it possible for me to become productive quickly. As it was originally intended to be a replacement for JavaScript, the language designers did an amazing job building a concise language that is easy to learn without replicating the broken design and weird behaviours of its archetype. Unfortunately it didn't succeed on the web. It only took off because of Flutter.

By allowing the language to be both compiled and interpreted, it is possible for apps to perform great in production without losing the option to hot reload an app during development. This made the code & run cycle in Flutter way shorter than with native apps.

Lessons learned

  • Being "boring" can make a language a lot easier to learn
  • The importance of a quick feedback cycle during development
  • Great tooling is more important than many language features

Off the beaten path

After spending more than a decade with OOP languages, I got tired of running into the same architectural issues over and over again. So I ventured into more unfamiliar territory.

Lua

Lua is a tiny language that is embedded for scripting in many other programs like games, servers and tools, but it can also stand on its own. I came across it many times before I finally decided to properly learn it.

The number of features the language doesn't have is remarkable. Its clever design still allows you to basically build everything. It can also perform very well with a JIT compiler.

The only data structure available is a table (which is another name for a map). Trying to solve problems in Lua makes you realize that this is the only data structure you need. Everything else is just sugar on top. It gave me a deeper understanding of the importance to pick the right data structures for an algorithm, and how it can affect performance.

Creating such a clean, powerful, and compact programming language is a tremendous achievement, and great credit is due to the designers for their remarkable insight and skills.

~Joseph Manning

Lua still has a special place in my heart, because it is living proof that a lean and clever design trumps any number of features. Also, it can be used to build games with many fantasy consoles and game engines. A bit unfortunate is the fact that its ecosystem is quite fragmented, with many different versions and runtimes being used.

Lessons learned

  • The importance of data structures as opposed to logic
  • How maps can be used to implement all data structures
  • Clever design beats features any time

Rust

Learning Rust was a very different experience than anything else I had learned before. It made me feel really stupid until I internalized the concept of ownership. Having gained that awareness, it permanently changed the way I write code today. I would recommend learning Rust to every experienced programmer, even when you don't plan on using it. It will make you write better code.

That being said, I don't particularly enjoy writing Rust. A lot of the libraries in the ecosystem are still beta and their documentation is often a bit sketchy. The compiler is slow, making the code & run cycle a practice of patience. But worst of all, Rust makes you write all code as if it was production code. You have to think about ownership and lifetimes, even for throwaway code, making it impractical for experiments. This really made me appreciate garbage collected languages.

With all those drawbacks, Rust is still the best language known to me to write efficient programs, making it a staple for systems programming. The type system is awesome, it has modern tooling, and it aids in writing stable code. Providing safe access to a realm which I always avoided, because I didn't feel comfortable with manual memory management, is a quantum leap.

Lessons learned

  • Learning a language can affect the way you think in other languages
  • The importance of ownership when you deal with shared mutable state
  • The merit of garbage collection in high level programming languages

Elixir

Learning Elixir was an absolute game changer for me. All of my previous experience with functional programming was very theoretical. Elixir changed that completely. Instead of putting algebraic data types front and center, Elixir focuses on immutability. It takes some time getting used to writing code without mutations, but it is worth it. It completely removes the burden of having to worry about shared state, because there is none. The same way Rust introduces a new concept you will think about when writing code, Elixir takes away a whole concern.

Going back to imperative languages feels like programming in paranoid mode.

~José Valim

Learning Elixir also made me realize the advantage of separating data and logic. Being able to treat data just as data, without having to consider possible side effects is huge, but requires immutability.

Having a pipeline operator to push data through a chain of function calls allows you to write concise code without the need for mutability. This is something I dearly miss in many other languages, just like powerful pattern matching, since I learned Elixir.

I also learned to appreciate dynamic typing again. Static typing is indispensable when everything is mutable, giving you at least some guarantees about the data you are working with. But sometimes it makes expressing certain things, that should be easy, unnecessarily complicated. One example is parsing JSON. With immutability, the need for static typing becomes less crucial. It makes me rethink my devotion to statically typed languages, which I had for the last 15 years.

Lessons learned

  • Data and logic must be kept separate to keep code clean
  • Immutability is one of the most important concepts in structured programming
  • With immutability, circular references are impossible, making garbage collection easy and fast

Verdict

It is hard to judge a book by its cover, just as it is hard to judge a programming language by its syntax. The greatest value of learning a language lies not in the language itself, but in how it transforms your thinking. You never know how a language might broaden your thinking until you tackle real problems with it. So don't let yourself get alienated by an unfamiliar surface.

But be aware, it can also spoil you if you are forced to go back to a language you don't enjoy writing. Good luck.