š§ Ctrl-Alt-Lang: How I Learn a New Programming Language
There are a million blog posts on how to "pick up" a new language. Most of them feel like they were written by someone who never shipped code at 3AM with PagerDuty breathing down their neck.
This is how I do it ā battle-tested, DevOps-brained, and tuned for curiosity over ceremony.
āļø Step 1: Compare and Port
I always start with what I know. Usually Python ā expressive, familiar, and fast to prototype in. I take a small script, something like this:
# python: count words in a string
def word_count(s):
counts = {}
for word in s.split():
counts[word] = counts.get(word, 0) + 1
return counts
print(word_count("grzly is grzly and grzly is sharp"))
And I port it to the new language. Letās say Rust:
// rust: count words in a string
use std::collections::HashMap;
fn word_count(s: &str) -> HashMap<&str, usize> {
let mut counts = HashMap::new();
for word in s.split_whitespace() {
*counts.entry(word).or_insert(0) += 1;
}
counts
}
fn main() {
let counts = word_count("grzly is grzly and grzly is sharp");
println!("{:?}", counts);
}
Then I go line-by-line and dissect the differences. Rustās HashMap
, ownership quirks, how it handles string slices ā all of it. I donāt move forward until I understand why the translation works the way it does.
ā” Pro tip: this is where LLMs shine.
āHereās my Python script. Port this to Rust and explain each change.ā
𧬠Step 2: Feel the Syntax in Your Body
Once Iāve ported a few examples, I sit with the syntax. I ask myself:
- What feels intuitive?
- What feels awkward?
- Where is this trying to protect me?
- Where is it asking me to trust the runtime?
When I first learned Elixir in 2018, it felt like a complete break from Ruby. But under the hood, it had the same heart ā clean, readable, and obsessed with developer joy.
# ruby
def greet(name)
"Hello, #{name.capitalize}!"
end
# elixir
defmodule Greeter do
def greet(name) do
"Hello, #{String.capitalize(name)}!"
end
end
Pattern matching, functional purity, pipelines ā it was all new. But it wasnāt alien. Just...reoriented.
The trick is to let yourself feel where it clicks and where it resists. Thatās where real learning happens.
š§° Step 3: Tooling Is Culture
This is where syntax tourists drop off ā and where DevOps folks level up.
Once Iām comfy writing code, I ask:
- Whatās the package manager? (
pip
,gem
,cargo
,go mod
) - How do I containerize this?
- How do I make the image tiny and fast?
- Can I get it live in CI/CD, in K8s, with observability baked in?
Letās take Rust with AWS as an example. Hereās using the aws-sdk-s3
crate to list buckets:
use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::{Client, Error};
#[tokio::main]
async fn main() -> Result<(), Error> {
let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
let config = aws_config::from_env().region(region_provider).load().await;
let client = Client::new(&config);
let resp = client.list_buckets().send().await?;
for bucket in resp.buckets().unwrap_or_default() {
println!("Bucket: {}", bucket.name().unwrap_or_default());
}
Ok(())
}
It's async, safe, and fast as hell. The SDKs feel native ā not bolted on. And once I can get this running in a multi-stage Dockerfile with a production-ready base, I know Iām in business.
š§ Real learning = understanding how it plays in the full SDLC:
From code ā container ā CI/CD ā runtime ā observability.
š Step 4: Write Until Itās Yours
Eventually, I stop porting and start building.
No more toy problems. No more ālearning projectsā that never leave the terminal. I start making things I actually want to use. Things Iād be embarrassed to show in public if they didnāt run clean. Tools I reach for in the dark at 2AM when the logs are screaming and the coffee stopped working hours ago.
A parser.
A CLI.
A microservice that doesnāt do much except expose the limits of what I donāt understand ā and make me learn anyway.
At first, it still feels like mimicry. Like youāre faking it. Borrowing someone elseās accent. You hesitate before every function. You over-comment your own thoughts just to remember what the hell you're doing.
But you keep going.
You write code on the train.
You refactor in your sleep.
You catch yourself dreaming in the syntax ā muscle memory before meaning.
And one day, the language talks back.
Not like a tutorial. Not like some sterile, documented best practice. But like a voice in your head. Half instinct, half ghost.
You start reaching for its idioms without thinking. You stop asking how to do something and start asking what it wants to say.
Thatās the real shift. When the language stops being a set of rules and becomes a palette. A scent. A weapon. A friend.
You donāt just write it ā you inhabit it.
You bleed in its patterns.
You argue with its design decisions.
You learn to love its flaws, not because you have to, but because you finally see what itās trying to do.
Thatās when you know.
Youāve crossed over.
Youāre not learning the language anymore ā youāre thinking in it.
ā Final Thought
Every language has a vibe. A rhythm. A set of tradeoffs itās asking you to buy into.
You learn the most not by memorizing the syntax, but by interrogating the philosophy.
So port something you know. Dissect every difference. Feel the friction.
Then ship it, run it, scale it, and let it break.
Because if it can survive your brain, your CI pipeline, and your Kubernetes cluster ā youāve really learned it.