Image by Kyle Hale
This is my final blog post in a three-part series that covers my approach to Execution. If you got this far after reading Ways of Working & Ways of Building, the good news is that we only have one more to go. If you haven’t, I would recommend checking them out. In this final post, I would like to turn our attention to the aspect of planning and how it plays a big part in how we set ourselves up for future success. Without further ado, let’s get started.
Ways of planning
Risk is inherently a part of any project. Successful outcomes are usually determined by how well we plan to mitigate it. We can unlock deterministic results in any project by paying down uncertainty early. Given a plan of execution for a project, we can set ourselves up for success by making sure that we spend some time identifying blocks of work with unknown answers. These are usually easy to track down if we plan ahead. Let me illustrate this with an example. Say we want to add a new server-side payment provider to our existing set of providers and an engineer comes up with a task like:
Integrate new Third-Party payment SDK
This task contains some inherent risks, so we can identify them by breaking the task down into its individual components:
Define an interface that integrates with the new payment method:
On first glance, this subtask could be considered risk-free. The calling frontend could include a field that denotes the new payment provider and the server can handle the request accordingly. If the complexity on the server is known then we could unblock this stream of work by mocking the server response.
Initiate a request and response with the new payment method:
Because this task could introduce risk, let’s work through them one at a time:
- This new endpoint could be asynchronous while the other endpoints aren’t.
- This new payment method may need a two-factor authentication mode that we currently do not support.
- The response from the new endpoint could contain insufficient information for the server to record a proof of purchase.
- The new fields introduced by this new endpoint may break standardization across the other third party providers.
Given these unknowns, all you need to do is to lead discovery and research into each before committing to the execution plan. As always, there will be situations where risk will only present itself once a certain body of work is completed. In cases like this, it is beneficial to flag these risks upfront and provide a worst-case estimate or decide on a backup plan B.
A general rule for any engineer working in a codebase is to leave it in much better shape than how it was when you started. When put into practice, caring about the health of the codebase allows you to unlock productivity in the future. At the start of any project, it’s worth considering items like tech debt and refactors to make our lives easier in the long run. Engineering-focused projects aren’t always considered because we as engineers do not spend nearly enough time making a case for the quality of our codebase. With a little effort, we can justify the investment by getting creative with how we measure impact. A few examples of measuring impact include feature completion time, code stability, bugs reported, and developer happiness.
Getting into the habit of frontloading potential refactors to projects helps us iterate our way to a healthier codebase. As you might have guessed, collaboration with the product team is important as both engineering and product need a certain degree of trust before investing time and effort into these initiatives. Future features that make use of newly refactored code serve as valuable data points to further strengthen the case for continued investment. When successful, initiatives like these are seldom celebrated, so make sure to call them out as something the team can be proud of.
A quick note around deciding on the right initiatives: We as engineers have a tendency to exaggerate the importance of a particular refactor or tech debt initiative just because some component is not correctly architected or does not follow the right design patterns. Over indexing on refactoring components does not always directly translate into product impact. In some situations, it is okay to let some components be, especially if they do not significantly improve developer productivity or they largely remain unchanged in the near future.
Setting up for the future
Setting up your team for long-term technical success may need some level of clairvoyance to make sure the team is well equipped for the challenges down the road. A simple way of going about this is imagining your team in the future. What would “they” be working on and what would “they” need? There are two valuable data points that help answer this question:
Having a good understanding of where the company is going is a good indicator into what is needed from an engineering perspective. Using the vision as a reference you can build out a technical roadmap to realize the products of tomorrow. Having a clear product vision basically means looking at the state of the codebase to make a decision on components that need to be built, or those that need to be updated to support future use cases.
Technologies of the future
The way we build software is always constantly changing. We therefore need to keep ourselves informed about new developments that prove beneficial in the long run. When we get into the practice of evaluating technologies on a regular basis we may eventually end up finding one that truly solves for a particular problem area. When done effectively, finding and using the right technologies early allows for positive change sooner while providing for the opportunity to lead the wave for collective mass adoption.
Examples of this in practice are prototyping with experimental alpha or beta frameworks, setting up an R&D practice to look into unlocking new possibilities, doubling down on features that drive user retention, and modularizing the codebase for future product teams. Ultimately, you know where your team is headed so use that information to chart a way forward.
This closes out the final section of this three-part blog series. It turns out that I had a lot more to say about execution than I had initially anticipated. My hope is that in reading my perspective you have a better understanding of your approach to technical execution. If you found it useful, I am happy to have helped. If not, I would like to hear from you as it serves as something to learn from. At the end of the day, all that remains to be said is that technical execution is something that I find fun and challenging; most of what I’ve learned comes from my curiosity of wanting to know how things work and making them better.
Feel free to get in touch with me via LinkedIn
A special thanks to the following people who helped get this blog series published:
If you find this kind of work interesting, come join us!