From competence to mastery
1229 words | 6 minutes readThis is an attempt to describe how I view competence and mastery within the software engineering discipline. These may sound like biased opinions (and they are), but with more than a decade of experience and having worked with many peers, I’ve come to appreciate the nuances that make for great engineers.
If you’re just getting started with your career, I recommend gradually building your way through these topics. There’s no rush – it may take years to get there. If you’re a mid-level engineer, consider which areas you’re not paying enough attention to. If you’re a senior engineer, consider if you’re missing out on something.
-
Keyboards are an extension of you, the earliest lesson on the job. Stick to a single keyboard layout (en-US) and master it. I’ve seen mid-level engineers struggling to make changes to a typescript function, not because the language was new to them, but because they scanned the keyboard with their eyeballs as they were looking for the right combination of keystrokes to type a special character. A professional software engineer should be able to code without looking at the keyboard and fluently type special characters without hesitation. If this resonates with you, do something to fix it.
-
Make the terminal your home, there’s no better tool to master first on the command line than Git. Learn to manage your stash, cherry-pick, reset changes, interactive rebasing, interactive addition, conflict resolution, creating and applying patches, etc. All of these build on top of modal edition, as you can quickly edit messages, resolve conflicts or prepare rebases, without being handcuffed by a clunky graphical interface, and having to constantly jump between keyboard and mouse. Git’s the bare minimum. Continue expanding your toolset, learn how to use cURL, jq, less, sed, ripgrep, etc. If you’re not familiar with the command line tools, you’re missing out on plenty of situations to be more effective. To illustrate my point, I regularly cross paths with engineers who seem alien to the superpowers of their terminal, for example, without relying on reverse-i-search (Ctrl+R) to quickly bring a command up.
-
Embrace modal edition, I’ve come to appreciate that interfacing with computers is the biggest bottleneck towards getting things done. We think faster than we can type. Modal edition gives you that extra boost to stop fighting the editor. Being familiar with the most basic motions will already make you a more effective programmer. The motions are so widespread and universal, that virtually all editors support them with extensions. Ignoring them feels, to me, like choosing to not learn English. I haven’t met anyone who got comfortable with them and wanted to go back to the old ways.
-
Learn a set of consistent shortcuts, they compound over time as your muscle memory builds up. For example, a long time ago I picked a set of shortcuts that felt natural to me for navigating terminal tabs, splits, creating vertical and horizontal, etc. It all started with iTerm2, then moved to Kitty, Wezterm, Ghostty, and never sacrificed any productivity. No matter which tool I was using, I kept the shortcuts consistent. For your editor of choice, learn the shortcuts for the file picker, the symbol picker, the common refactors (like renaming), the one that creates the test file automatically, etc. I regularly encounter engineers unaware that Ctrl+W deletes the previous word in the terminal, or that Ctrl+A/E brings you to the beginning/end of the line, instead they delete characters one-by-one. Please learn the shortcuts.
-
Do not hesitate to debug, I really want to find the bug in plain sight, it’s now been 15 minutes and I’m making no progress. Stop being stubborn (and lazy) and spin up a debugger. For some reason, I always experience some initial reluctance to start a debugging session, yet I’ve never had a single regret for having done so. It’s saved me countless hours of banging my head against the wall.
-
Write idiomatic code, competence is knowing the most natural way of getting things done with the language at hand. Unless you have good reasons to do so, why would you manually keep an index to iterate a list? If your language provides nicer constructs to do that, use them. With Go, you use
rangeto unpack a tuple with the index, in Python you pass an iterable toenumerate(...), in Rust youenumerate()your iterator. Does your language provide slicing? Learn to use it. List comprehensions? Master them. There’s nothing more frustrating than constantly fighting the language to get simple things done, invest in learning idiomatic ways of getting things done. -
Stop producing obvious technical debt, follow principles of single responsibility, you already know this class is doing too much — you noticed because you realized the component’s testability sucks. Don’t persist with dead-end approaches, and actively look for composition opportunities, and define clear boundaries and interfaces. When struggling, focus on solving the problem first, generalize later after getting a better understanding of the problem.
-
Stop producing big PRs, you start working on task A and realise there’s some tangential task B that must also happen. What’s stopping you from opening a small PR with B, instead of pushing for A+B? You’ve already mastered the git cli and should be able to quickly stash changes, checkout new branches, apply patches, cherry-pick stuff, etc. Keep building on top of your already existing skills. Small PRs are not only faster to review; they result in quicker feedback loops, and prevent you from building things on top of flaky foundations.
-
Practice concurrent programming, I’ve seen senior engineers without a decent grasp of concurrency and thread safety. If you are not getting exposed to these problems in your job, then expose yourself to them in your pet-projects. Start practicing mutexes, atomics, threads, figure out how to safely parallelize your code, etc. This is foundational – don’t sleep on it. Don’t settle for knowing only the theory, any senior worth their salt should have battle-tested experience with concurrency and parallelism.
-
Learn other programming languages, I took way too long to get into more programming languages, something which greatly expanded my skillset. Learn Go and its powerful concurrency model. Learn a functional language and code a data-structure in it. Try Rust and see what memory safety looks like, etc. Exposing yourself to new paradigms will greatly contribute towards your growth as an engineer.
-
Side-projects can only make you better, once or twice a year, I’d get an urge to embark on some technical pet project that dispels the magic behind some topic I know very little about. Computer science is vast, with endless opportunities to learn. I’ve become a better engineer just by reading the DNS spec and building a basic server for it. Crafted lexers and parsers for the first time. I’d have never believed I’d be able to put together a chess engine from scratch. I realized one day, that I knew nothing about embedded systems, so I jumped into that. The same for storage and filesystems, so I built a filesystem. Side projects have been — and continue to be — a critical source of knowledge and expertise that I’d have never gotten from my job.
I’ll stop the ramble here. I hope something here resonates with your personal experience, or perhaps inspires you to change how you approach the craft. You might also disagree with many of the points, and that’s alright.