QIAOPANG.COM · SELECTED ESSAYS (2013–2026) · 260 ESSAYSBrowse Chronologically (2013 → 2026) ↗
QiaoPang · Programmers LifeSelected Essays · Systems, Craft & Agentic Era
Timeline ↗

The Languages I Chased in Those Years

The Languages I Chased in Those Years

Your humble programmer was young once, and the price of youth is blind devotion.

It started with MS-DOS 6.0: from that moment on, your humble programmer was a fanatical Microsoft loyalist.

That fervor peaked with Windows 95 (raise your hand if you still remember the “Good Times” music video bundled on the Windows 95 CD), carried on through The Road Ahead and Windows 2000, and finally hit its zenith when .NET was released.

In those days, whatever Microsoft opposed, I opposed.

I disliked Netscape Navigator purely because of IE. I cursed SUN and loathed Java with a passion, because the NC was the mortal enemy of the PC and SUN was out to guillotine Microsoft. I wrote Visual Basic and gnawed through MFC for no other reason than that they were powered by Microsoft.

But VB was too weak (really, my skills were just too poor), and MFC was too messy — so much so that in my sophomore year, while doing contract work building software for someone, I grudgingly chose Delphi.

I never much liked austere Pascal, but Delphi gave me reasons I couldn’t refuse. Writing code in it felt fresh and crisp; in productivity it lapped the lumbering MFC by several blocks, and in speed it made VB look clumsy by comparison. Back when Wake-on-LAN was still a novelty, the little feature I remember most fondly from my Delphi days was turning dozens of machines on a LAN on (or off) from a single computer. Watching my software “magically” wake a whole roomful of machines in the lab — I was proud beyond words.

During my university years, building client software (or C/S applications) still made money, but it was steadily losing its cool. The arrival of ASP moved my interests over to the web (the fashionable term back then was B/S). The rise of ChinaRen planted the idea of building my own class homepage, but what I built could only sit on my hard drive and be demoed a couple of times when I entered competitions — back then there was practically no free server offering MSSQL, and every one of my “websites” was built on MSSQL or its cut-down cousins. I was an ostrich with my head in the sand, confining myself to the little world of programs I had constructed.

Then .NET arrived, showered with Microsoft’s ten thousand favors, and I embraced it immediately. I played with C# code while continuing to ignore the then-ascendant Java 2 and the insufferably swaggering J2EE. C# was enchanting — it instantly gave me the urge to throw Delphi out the window — but the .NET Framework was just too big (and relatively slow at the time), and customers still running Celerons simply couldn’t take it.

Nothing is more painful for a programmer alive than this: the language you love most (C#) can’t put food on the table, while the language you don’t much like (Pascal) is the one that pays your bills.

C# couldn’t stretch its legs in my part-time work; it became just another competition language, another research-lab language, something I waved around to impress people (back then, talking about .NET was like talking about cloud computing or big data today — it was easy to bowl over anyone who didn’t know better). I wrote fictional “enterprise information integration systems” that even I didn’t believe in, and churned out papers in assorted core journals.

After graduation I’d intended to find a C# job, but by twist of fate I ended up in telecommunications, where C replaced C# as my mainstream language. In university my C was barely at the textbook-exercise level; the hardest exercise I’d done was implementing inodes to simulate a simple Unix filesystem and then provide a few shell commands to create directories and create and modify files. On the job, sockets, timers, hash tables, and rings finally stepped out of the textbooks — and alongside my telecom work, Linux formally entered my life.

Once I grasped Linux’s power and the beauty that radiates from its very bones, I drifted further and further from Microsoft’s technologies, and also left CodeProject, where I’d hung around for years.

Life writing C is dry, especially writing protocols in C. The one I wrote end to end, IGMPv3, was mostly a brainless translation from protocol prose into C.

Those two years were exactly when the internet was blossoming. Google was growing at full tilt; Baidu was moving from being Sina’s search provider (2B) to seeking out the mass market (2C); 3721 came preinstalled in every browser; and MediaWiki rode the wave of Wikipedia’s popularity. PHP — the language MediaWiki was written in — became the object of my spare-time study. LAMP was becoming the fashionable acronym, and WAMP/Apache Friends offered a complete internet development environment for people still stuck on Windows.

Later, while building an automated test system, I felt PHP straining at its limits — I was missing a good glue language, something that could move fluidly between web and system. It happened that a saying had long circulated in the trade: “Real programmers use C; clever programmers use Python.” So I learned Python too. The jigsaw was complete: PHP on the front end accepted tasks submitted by users; Python picked the tasks up, checked out the corresponding full set of router source code from ClearCase, compiled an image, then used pyserial (a serial-port library that can talk to a router’s console) and pyexpect (a Python wrapper around expect) to connect to the routers in the test bed and load the compiled image, and finally invoked the automated test scripts supplied by the test team.

There was no kill-switch tool like RabbitMQ back then, so the task synchronization between PHP and Python was thoroughly primitive: PHP inserted the task into the database, and the Python program sat in a dead loop reading tasks out of the database every 30 seconds.

Later I moved to the foreign company where I work now, and on a colleague’s recommendation I dabbled briefly with Qt. Qt’s slots and signals are genuinely beautiful — but by then client software had been struck entirely from my skill list, so I never pushed further on Qt.

In that period, C fed me, PHP kept me connected to the web, and Python remained my favorite for building all sorts of small tools.

Along the way I played with Drupal and Symfony. Watching the Symfony author’s screencasts was how I learned that there existed a development marvel called TextMate, and that a certain kind of programmer’s computer was called a MacBook.

Symfony was a fine evangelist for me: it introduced me to Ruby on Rails and Django (by way of comparisons between Symfony and the two of them).

One of its tutorial projects, Jobeet, also gave me ideas for naming my future startup — yes, that’s exactly how Toureet came about, and the name had been lodged in my mind since around 2007. It isn’t a particularly down-to-earth name, but like a first love, it simply wouldn’t go away.

Once I learned about Ruby on Rails, I realized the era belonged to RoR sweeping all before it — practically every startup was using RoR. Robin of JavaEye claimed he’d stood JavaEye up in a matter of days. I didn’t spend much time on JavaEye, but it still jolted me hard, and I paid deep homage to both Robin and RoR.

But at the time RoR had been internally split for a while, and the community was just beginning to think about how to get the two forked branches to bury the hatchet and reunify in RoR 3.0. That put me in a foul mood: should I wait, or not wait, for that far-off RoR 3? After all, Symfony 2 had already burned me badly once — the code I’d written on 1.x wouldn’t run on 2, and 2’s changes were so drastic that I couldn’t adapt for a while. If I started learning RoR 2.x now, would I be repeating the same mistake?

Of two evils, choose the lesser, and so I settled on Django.

Then, through a mix of lucky coincidence and various inevitabilities, I set out on the path of entrepreneurship.

By the time I “graduated,” I could count myself a competent hand at Django and JavaScript.

JavaScript is a fascinating creature. Around 2000 I copied (yes, copied) my first snippet of JS — a greeting script that checked the current time and offered different greetings accordingly. Silly and naive.

Back then JavaScript’s reputation stank. Beyond being fun, nobody took it seriously.

It wasn’t until a few years later, when prototype.js and script.aculo.us appeared (who remembers you now? sigh), that JavaScript began to clear its name.

Then came the godlike jQuery.

Next, JavaScript, now full-fledged, started playing with MVC, whose flagship was backbone.js.

It also played with functional programming, as in underscore.js.

In short, by the time I started my company, JavaScript’s ecosystem was bursting with life. Toureet’s first product, the itinerary editor, used backbone.js; the second product’s entire front end adopted ember.js, written in CoffeeScript.

I wrote a Makefile to concatenate, compile, and package all the CoffeeScript.

I don’t think I’ve mentioned Makefiles, have I? A programmer had better know Makefiles — they take a great deal of busywork off your hands.

Then JavaScript, riding on V8, ushered in the Node.js era, and Node.js carried JavaScript through the front door to become a formidable force on the back end.

From then on, front-end engineers began their underdog counterattack and became hot property. Someone who knows Python may not dare to write front-end code, but someone who knows JavaScript is already conquering territory on the back end.

Clunky XML gave way to JSON, and data communication between front and back ends fell into JavaScript’s grip.

MongoDB’s arrival further fanned the flames — even the database stores JSON (BSON), with JavaScript as the stored procedure (that’s an easier way to think of it). What, at this point, can’t JavaScript do?

In the space of a year or two, LAMP was overthrown and replaced by MEAN (MongoDB, Express, Angular, Node.js/Nginx), and Node.js looked every bit as poised to sweep the board as RoR once had.

Built on Node.js and championing React, meteor.js blew a fresh breeze across the whole internet — so a website’s code really could make everything come alive that nimbly! After a few evenings of hasty study, I wrote teamspark, which became the standard internal communication tool for the Toureet team. Unfortunately, meteor.js somehow hit a bottleneck in its development, and the solid user and market base it had built was whittled away bit by bit through two years of slow iteration.

Later I moved from the internet back into telecom, and during a rare stretch of leisure I studied concurrency properly. And concurrency isn’t concurrency without Joe Armstrong’s Erlang. Compared with the languages I understood, Erlang had a distinctly otherworldly air about it.

From Erlang I greedily extracted a great deal of knowledge. Erlang is hard to adapt to — especially when you want to express x=x+1 and feel such agony and helplessness. It took me a fair amount of time to figure out how atoi() is actually implemented in Erlang.

Though I have yet to write any decent system in Erlang, its impact on my thinking was enormous.

Eventually I extended from Erlang to Go. Go is a language whose virtues and flaws are equally prominent. I originally regarded it as a “modern C” with built-in concurrency support, but the deeper I went, the more I realized Go and C belong to two different worlds. Go can replace C in certain scenarios, but it cannot supplant C. Soft real-time is a curse that shuts almost every GC-enabled language out on the far side of C. As things stand, perhaps only Rust could in theory replace C/C++.

Go has already reached version 1.2, but it still has a long road ahead. First, its compile speed is considerably slower than advertised, and its execution speed is far behind C — in many scenarios (especially GC-related ones) it even trails JVM languages like Scala.

Go has plenty of bugs, too — even I could find a serious one that bloated executables to hundreds of megabytes (the bss segment wasn’t handled properly).

But Go’s exquisite syntax, clean ideas, and language-level concurrency support keep it an object of continual study for me.

A person’s energy is finite, after all. Many languages I merely sampled for their ideas, wrote a few simple examples (a hundred-odd lines), and set aside. My thanks go to: Ruby, Elixir, Io, Emacs Lisp, Scala, Nimrod, Rust, Clojure, and Lua. I absorbed a great deal of interesting knowledge from you, yet only dipped a toe in and never went deeper to interact with you.

You may ask: how long does it really take to master a language?

To master one language well may take three to five years — perhaps longer.

But once you have mastered one, picking up a second should take about a week to get started (Erlang, Haskell, and Lisp excepted).

These days I no longer hold any particular prejudice against any company’s technology. I embrace anything that raises productivity.

You may ask: what’s the use of learning so many languages?

If it’s to feed yourself, mastering one is enough. The rest are useless — mere idle amusement. What I value is the thinking behind a language, and I enjoy the pleasure of developing in different languages by comparison. Besides, when a language you love adds a new feature (or you use some advanced feature), you can immediately see the logic behind it: ah, so this is really the idea from such-and-such a language. And with that, your grasp of both languages deepens a notch.

My greatest regret at present is that I’ve never calmed down and properly looked at Java.

Though I hold no prejudice against technology, I still loathe the vile IE 6/7; and at the same time, there is one thing I love from the bottom of my heart: the Mac.

If I had to offer a programmer one piece of advice, it would be this: programmers should use an MBP.

MORE IN Languages & Craft

Further Reading in This Column

View all essays in “Languages & Craft” →

Time to Meet jj: Git's Next Evolutionary Step

Git has ruled version control for twenty years, but its staging area, brittle rebases, and blocking merge conflicts are becoming liabilities in the AI era. jj rebuilds the mental model from the ground up — while staying fully Git-compatible. Here's why it deserves your attention.

New Hope (2025–2026)5,588 chars · 12 min read