Technical Excellence
The foundation of becoming an excellent product engineer starts with technical excellence. Before considering anything else, you need to be proficient in engineering, knowing the basics of your tools and programming languages, and understanding how to complete a project within a given scope. But excellence doesn't stop there.
Personally, one book has been incredibly influential in my engineering journey: The Pragmatic Programmer. I read this book once every few years at minimum, and each time I discover something new and applicable. I highly recommend it if you haven't read it already. While I can't share everything from this book, I'd like to highlight a few topics particularly relevant to product engineering.
Programming is a Craft
One chapter emphasizes that programming is a craft. This means you need to think about how you work, not just what you work on. This was a mind-blowing revelation for me. I don't just have to finish projects as quickly as possible; I should also reflect on my work process and look for ways to improve.
The Cat Ate My Source Code
The book also discusses the concept of "the cat ate my source code." Obviously, this can't be an excuse when source code disappears. We have tools like version control, so if your code is lost, it's your responsibility, not your product manager's or designer's fault.
This section also addresses responsibility more broadly. Responsibility is something you actively agree to take on. When you see that something clearly isn't achievable, it's your responsibility to say "no, this can't be done" and explain why. But you shouldn't stop there. Try to find alternative options: "We can change this aspect of the project, and then it might take one month instead of six months."
Software = Soft + Ware
Another important aspect covered in the book is flexibility. I like to think of software as "soft" plus "ware". "Soft" meaning flexible and easily changeable, and "ware" referring to a product that delivers value.
It's easy to focus solely on the feature perspective of software since you need to deliver new capabilities. However, we often forget about the "softness" of software, maintaining its flexibility so that when major changes are needed, you don't spend months implementing them. You want to keep your codebase as flexible as possible, minimizing the time required for significant changes. The book offers several approaches to maintaining this flexibility:
DRY–Don't Repeat Yourself
(Or, WET–Write Everything Twice)
The principle of "Don't Repeat Yourself (DRY)" means that if you need to change something, you should only need to change it in one place. Having to modify multiple locations for a simple change takes more time.
The "Write Everything Twice (WET)" principle has emerged as a counterpoint. Sometimes, trying to avoid repetition can lead to over-optimization. It might be acceptable to do the same thing twice, but by the third time, you should create a function or another mechanism to avoid writing the same code repeatedly.
Orthogonality
Another concept from the book is orthogonality. In geometry, two lines are orthogonal if a change in one coordinate doesn't affect the other coordinate. In computing, two or more things are orthogonal if modifying one module doesn't introduce unexpected changes to another.
We should eliminate effects between unrelated components, making it easier to change one module without impacting others.
Reversibility
Reversibility is crucial because there are no truly final decisions in software development. For example, let's say your app displays two options for the donation. Can we assume we'll always have exactly these two options? Probably not.
When writing code, ensure it can handle one, three, or even more items (or zero, though that might be controversial, so let's stick with one or more). Your code should accommodate varying numbers of items, not just the current count. Once you achieve this flexibility, changing from two items to three becomes trivial.
Tracer Bullets
If you're familiar with tracer bullets from military contexts or first-person shooter games, you know they're inserted between regular bullets. At night, you might have ten normal rounds followed by one tracer bullet. While you can't see where the normal bullets go in darkness, tracer bullets emit light, showing you your trajectory. If you're not hitting the target, you can adjust your aim for the next set of rounds.
Applied to software engineering, there are two approaches to building software:
- The measuring approach: Plan extensively, do everything possible to prepare, take one shot, and hope it works.
- The tracer bullet approach: Build something rough that serves as a guiding structure. For example, when implementing a feature involving a new API response that needs to make an old UI dynamic, your first step might not be to build the complete feature. Instead, make a simple call to the endpoint (even with dummy data) and process the response, or create a skeleton view without content that can later host subviews. This approach is less risky than doing everything at once and hoping it all integrates correctly.
Prototypes
Prototypes differ from tracer bullets because tracer bullets typically become part of production code, while prototypes are usually throwaway code. Prototypes help you learn about user value or business outcomes by creating something rough in a short time.
The fun part about prototypes is that you don't need to worry about the coding standards you follow every day. You can feel the guilty pleasure as long as the prototype is working well enough to answer your questions.
Estimating
The book also covers estimation, acknowledging that it's difficult.
Another helpful insight is that estimation improves with practice. Recording your initial estimates, tracking actual time spent, and periodically reviewing the differences helps you identify patterns and adjust future estimates accordingly.
It's important to note that estimation should not be a rigid commitment. The best estimates provide product teams and leadership visibility into what can be accomplished within a given timeframe, helping them plan accordingly. However, as new information emerges, estimates can and should change. Good estimation helps teams decide which details to prioritize when time constraints arise.