C++ at Work, Lua for Fun

What C++ and Lua taught me about clarity, speed, and enjoying the craft.
Every developer has a main language and a comfort language. The main one is what you reach for when the work is serious. The comfort one is what you open when you just want to think out loud.
For me that’s C++ and Lua. They couldn’t be more different in philosophy, and that’s exactly why having both in rotation works so well.
C++: the language that makes you earn it
C++ does not let you be lazy. That’s not a criticism, it’s the point.
When you write C++, the machine is right there in the room with you. You know where memory lives. You know when a copy happens versus a move. You know that your abstraction has a cost, and the language is going to make you think about what that cost actually is before it lets you ship. There’s no garbage collector quietly saving you. There’s no runtime making things “just work” behind the scenes. What you write is very close to what runs, and that proximity is what makes C++ so valuable for systems work and anything where performance actually matters.
That discipline has changed how I think about software even when I’m not writing C++. Before I add a dependency, I ask what it actually does. Before I layer another abstraction, I ask what it costs and whether the simpler thing would work just as well. Before I declare something “fast enough,” I ask whether I’ve actually measured or whether I’m just guessing.
#include <vector>
#include <algorithm>
#include <numeric>
// no allocation, no copies, just a linear scan
int sum_above_threshold(const std::vector<int>& values, int threshold) {
return std::accumulate(
values.begin(), values.end(), 0,
[threshold](int acc, int v) {
return v > threshold ? acc + v : acc;
}
);
} That’s not a fancy function. But the habit of asking “is there an allocation here? is there a copy? what does the iterator model cost?” is exactly the kind of question C++ trains you to ask automatically. And those questions are useful in every language, not just C++.
The things C++ has taught me that I carry everywhere:
Interfaces should be small and explicit. If a function takes five parameters, it probably wants to be two functions. If a class exposes fifteen methods, most of them are probably implementation details that leaked. C++ doesn’t enforce this for you, which means you have to enforce it yourself, and that practice sticks.
Profiling is part of design, not a cleanup phase. In C++, guessing about performance is almost always wrong. The cache behavior, the branch prediction, the allocator behavior under load: these things matter and they’re not obvious. You learn early that assumptions are expensive and measurements are cheap.
Data structures before micro-optimizations. A better algorithm or a better layout almost always beats a faster implementation of the wrong approach. I’ve rewritten hot paths that turned out to just need a flat array instead of a linked list. The micro-optimization mindset is a trap C++ can lead you into. The escape is realizing that the structure is the optimization.
I also want to say something about modern C++ specifically, because the “C++ is terrible and you shouldn’t use it” discourse often references a version of the language that’s fifteen years old. Move semantics, constexpr, ranges, structured bindings, the modern standard library algorithms: the language has evolved significantly. The error messages are still a lot. The build system situation (yes, CMakeLists.txt, we meet again) remains its own adventure. But the expressive power of well-written modern C++ is real, and for anything systems-level it’s still the right tool.
Lua: the language that reminds you this is supposed to be fun
Lua is my sketchbook.
When I have an idea I want to test quickly, or a small script that needs to exist, or a concept from a math class I want to poke at, I open a .lua file. No headers. No build step. No linker. Just lua script.lua and the problem.
Lua is small by design. The whole language fits in your head. Tables are the only data structure and they cover everything: arrays, maps, objects, modules. The standard library is minimal enough that you can read the whole thing in an afternoon. That simplicity is a feature, not a limitation. When the language gets out of the way, you think about the problem instead of the language.
local function fizzbuzz(n)
for i = 1, n do
if i % 15 == 0 then
print("FizzBuzz")
elseif i % 3 == 0 then
print("Fizz")
elseif i % 5 == 0 then
print("Buzz")
else
print(i)
end
end
end
fizzbuzz(20) Yes, it’s fizzbuzz. The point isn’t the code, it’s the fact that I wrote, ran, and verified it in about ninety seconds without touching a terminal configuration or a package manager. That speed of iteration is genuinely valuable when you’re exploring an idea rather than building a system.
Lua is also where I play with math concepts outside of class. The math repo I wrote about in an earlier post is all Lua: a Mandelbrot set renderer, matrix addition, a Fibonacci benchmark. None of that needed C++. None of it needed a framework. It just needed a language that would run the code and show me what happened.
What Lua gives back is a certain lightness. After spending a week thinking hard about memory ownership and type safety and exception guarantees, opening up a Lua file feels like taking a deep breath. The constraints are gone. You can just build the thing.
-- a dead simple event emitter in about 15 lines
local Emitter = {}
Emitter.__index = Emitter
function Emitter.new()
return setmetatable({ listeners = {} }, Emitter)
end
function Emitter:on(event, fn)
self.listeners[event] = self.listeners[event] or {}
table.insert(self.listeners[event], fn)
end
function Emitter:emit(event, ...)
for _, fn in ipairs(self.listeners[event] or {}) do
fn(...)
end
end That’s a functional event emitter. No classes, no inheritance hierarchies, no boilerplate. Just tables and functions. Lua’s object system is opt-in and built from first principles, which means you only pay for what you actually use. There’s something philosophically satisfying about that.
Why having both matters
This might sound like “I like two things,” but I think there’s something more structural going on.
C++ and Lua sit at opposite ends of the control spectrum. C++ gives you total control and makes you pay attention to everything. Lua gives you almost no control over the machine and makes you focus entirely on the logic. Spending time at both ends of that spectrum makes you much more conscious of where you are on it at any given moment.
When I’m writing TypeScript for a web project, I’m somewhere in the middle: more control than Python, less than C++, more runtime than Lua, more structure than shell scripts. I can feel where I am. I know what the tradeoffs are. I make deliberate choices about what to reach for instead of just defaulting to whatever’s familiar.
That’s what I mean when I say these two languages make me a better engineer. It’s not that C++ and Lua are inherently superior. It’s that building fluency at the extremes gives you a much better map of the entire territory.
C++ keeps me honest about cost. Lua keeps the joy in the craft. Between the two, there’s a lot of room to build.