The Best Tool for the Job, 3 Years Later

The Best Tool for the Job, 3 Years Later

Three years ago, I wrote a post offering insight into how broad engineering decisions are made at FlightAware; specifically, it covered my thought process behind recommending a language we should unify behind for micro-service development. With this post, I look back on how the last three years of Go adoption have gone at FlightAware and how I would have approached my analysis differently with the powers of hindsight.

How's it Been Go-ing? (sorry)

I'd like to think my carefully considered words carried great weight when deciding what language to unify around, but in reality, I was just one voice in a choir of engineers singing the praises of Go. We put our best foot forward with its adoption: our most vocal Go advocate put together content for a reading group to help people get up to speed; language guidelines and standards were established; and of course a go-utils library was quickly created to act as a kitchen sink host core shared functionality you'd expect to crop up everywhere.

All that up-front investment has paid off! We've now implemented many key services with Go, such as our AuthNxt backend and key pieces of AeroAPI; the reading group has proven popular enough to repeat several times; and go-utils has not yet grown unmanageably large.

On a personal level, I've found Go to be a real pleasure to work with. To be honest just about any language with some semblance of typing would be refreshing coming from Tcl. I went in fully expecting to appreciate the tooling and the wealth of functionality (both in the standard and 3rd party libraries).

However, one aspect of Go that caught me by surprise was how much I liked the fact that it's Go all the way down. Some may find this detrimental, as it results in many libraries being reimplemented in Go when they have more widely tested, optimized, or canonical C counterparts. But for me, it means that when I want to peek at a function's implementation, I never have to think about whether I'm about to hit the "native wall" in an interpreted language where you're suddenly reading C (if you can find the implementation). Combine this with Go's enforced stylistic consistency, and suddenly the programmer is empowered to fully comprehend everything happening in their codebase, down to the nuts and bolts.

At times, though, I mourn what we lost with our commitment to Go. Before seriously considering Go, FlightAware was all-in on transitioning from Tcl to Python. To support this, we developed a library called tohil to let Python and Tcl code interact seamlessly. This gave us a promising path for incremental conversion. We could write new code in Python while still relying on Tcl libraries containing battle-tested business logic. Go derives much of its top-tier developer experience from being "Go all the way down". That same design makes the prospect of directly calling Tcl functions from within Go, while technically possible, unpleasant.

This brings me to the second part of the post.

Reflecting on the Assessment

My primary goals with the language assessment were to be thorough and objective, which drove my use of the five-category rubric. While the rubric was an effective tool, my focus on it left me blind to the elephant in the room: Tcl.

You'd be forgiven for not knowing what I'm talking about, seeing as I somehow didn't mention Tcl even once in the original assessment. You might even have gotten the initial impression that my assessment was focused on the language selection for a greenfield project, perhaps even a brand new startup. That could not be further from the truth.

Tcl is a general purpose scripting language with some interesting ideas, but its community and ecosystem stagnated compared to similarly positioned languages like Python or Ruby. This stagnation was not yet clear at FlightAware's inception back in 2005. More importantly, the founders already knew the language, so Tcl remained the language of choice for nearly all of FlightAware's code for over 15 years.

You'd think I would have included "Compatibility with our massive Tcl codebase" as a category in my rubric, which would have highlighted an area where Go is quite deficient for our specific use case.

Funnily enough, even if I had, I don't think it would have changed my conclusion. Go is extremely attractive for microservice development and has served us exceptionally well at FlightAware. Why is the rough Tcl interop worth mentioning in the first place then? If I'm picking Go anyway, don't I want to present it in the best possible light?

I think it comes down to making my intentions explicit. By highlighting a serious tradeoff in Go but selecting it anyway, I am saying, "Yes, I know it makes legacy integration harder, but here's why that's not a big deal (or perhaps even a good thing)".

What Have I Learned?

First, I've learned that there is a big difference between looking at your current environment and looking at your entire environment. In my original assessment, I explicitly noted that we couldn't evaluate these languages "in a vacuum" and made a point to factor in our existing footprint of Python, Go, and Rust. But while I successfully pierced the vacuum for our modern stack, I remained blind to our foundation. Our newer tools run alongside over a decade of mission-critical Tcl. Leaving that context off the rubric was a miss, even if it wouldn't have changed the outcome.

Second, I’ve realized that a clean break can be highly beneficial. If we had leaned entirely into Python and our tohil interop, we might have found ourselves permanently anchored to our legacy codebase, endlessly writing modern wrappers around calcified logic. Go’s stark boundary and its friction with Tcl has acted as a forcing function, compelling us to fully architect and modernize data systems like AeroAPI and execute clean backend migrations.

Finally, I’ve learned that engineering leadership requires explicitly documenting the pain points of the chosen path. Every technical choice leaves a scar somewhere. By acknowledging the friction of Go’s interoperability upfront, we establish trust. We are deliberately choosing which set of problems we want to manage and fully accepting the tool's imperfections. Three years later, I'm glad these are the problems we chose.

Chris Roberts

Chris Roberts

Chris Roberts is a Senior Software Engineer on the Backend Alpha Wing. He spends a lot of time thinking about how to make FlightAware's APIs more pleasant to use.

Show Comments
Back to home