Does the Language Still Matter When AI Writes the Code?
I picked up Ruby because of how expressive it was. Writing it just felt nice, in a way that other languages did not. Later I found out that was deliberate: Matz designed Ruby so that programmers would enjoy using it. I then spent the better part of a decade writing Ruby and enjoying it.
These days I am not really writing code. It gets written by AI and I read it. That shift happened faster than I expected, and it has left me with a question I cannot settle.
Does the language still matter?
I do not mean it in the usual sense. Not typed or untyped, not compiled or interpreted, not fast or slow. I mean something narrower: does it matter whether a language was designed for readability and enjoyment?
Why these two
Ruby and Go are not obvious rivals. Nobody is picking between them to write a Kubernetes controller this afternoon. But they bracket a shift I lived through, and they are the two languages I have written most of my code in.
A lot of the early cloud infrastructure was Ruby. Vagrant, BOSH, large parts of Cloud Foundry. I spent a couple of years on the BOSH OpenStack CPI myself. Then Docker and Kubernetes arrived with better answers to the same problems, and Go arrived with them. Cloud Foundry eventually began moving components to Go as well. So this is not an abstract pairing for me. It is the language I came from and the language I ended up in.
Two ends of the same table
Go is, in my opinion, the opposite of Ruby. I do not think it was made for the programmer to enjoy. It was made to be a lingua franca, something many developers across many projects could pick up quickly. A good enough solution, on purpose.
I am not trying to downplay it. There is real elegance in a lot of Go's design, it is extremely effective, and there are good reasons it became the default in the cloud native space. Its explicitness is a feature, and I rely on it every day. But it feels more like an industrial solution than an artistic expression. Those two languages sit at opposite ends of the same table.
Here is roughly what that looks like. Say you want the names of the nodes that are ready, sorted.
def ready_node_names(client)
client.list_nodes
.select(&:ready?)
.map(&:name)
.sort
end
func ReadyNodeNames(ctx context.Context, c Client) ([]string, error) {
nodes, err := c.ListNodes(ctx)
if err != nil {
return nil, fmt.Errorf("listing nodes: %w", err)
}
names := make([]string, 0, len(nodes))
for _, n := range nodes {
if n.Ready {
names = append(names, n.Name)
}
}
sort.Strings(names)
return names, nil
}
I want to be fair about what that comparison shows, because it is easy to rig this kind of example and prove nothing.
The Go version is not unclear. Every line in it is obvious, which is exactly what Go is going for. It also does something the Ruby version does not: it tells you what happens when listing the nodes fails, and it wraps the error with enough context that you will know where it came from. In the Ruby version that failure is invisible. An exception propagates up through code you cannot see from here. If I am reviewing the Go function I know how it behaves when the API is down. If I am reviewing the Ruby one, I have to go and find out.
That is a real trade, and Go's side of it is not boilerplate. It is the part I actually want to read.
The rest is. The make with a capacity hint, the range loop, the append, the second return value threaded through every caller. I have read those exact lines thousands of times. They carry no information specific to this function.
Why I think it matters more now, not less
Reading that boilerplate was never fun. But it was survivable, because there was only so much code a human could produce in a day. That is no longer the constraint. We can now generate far more code than we used to, and the reading has not gotten any faster.
I do not think I need to control every line anymore, or hold a whole codebase in my head the way I once did. I have made my peace with that. But I do want to be able to maintain software over the long term, and for that, review is still the thing that matters.
I am not claiming it always will be. Maybe we end up with other ways to judge whether a system is maintainable, and then none of this matters and I will happily stop reading diffs. But that is not where we are. Today, when a model gets something wrong, a person still has to go in and fix it, and that person has to read it first.
So while that holds, how a language reads matters more than ever. It does not have to be Ruby's expressiveness specifically. It has to be that the signal-to-noise ratio of a diff is high enough that I am reading decisions instead of scanning past ceremony to find them.
What I would want
I would love to see new languages designed for this: not for the joy of writing them, but for making code review easy and effective.
I have a rough idea of the shape. As little boilerplate as possible. Concise. Pleasant to read. And no special cases you have to master or get bitten by, because the reviewer is now often the person who did not write the code, and every subtlety is a trap for them specifically.
Maybe the code does not even need to be written in that language. Maybe it gets transpiled into it, and the readable form is the one you review.
I am well aware that models can review code too. I use that, and it catches things. But I have yet to see a fully lights-off factory that delivers maintainable code, the kind a human opens six months later without wanting to tear their eyes out. That is the bar, and nothing I have run clears it yet.
And while we are at it: could we please also come up with something better than YAML?
Anyway. What is your favorite language to review code in, and why?