When it comes to deploying code, I’ve often found that the frequency of our deployments can significantly impact the quality of our work and the overall health of our codebase. In my experience, when we don’t deploy very frequently, we inadvertently invite a host of dysfunctional behaviours that can be easily avoided.
The Pitfalls of Infrequent Deployments
One of the most common issues I’ve observed is the tendency to tackle larger chunks of work. Instead of breaking down a big feature into manageable pieces, we often try to deliver it all at once. This approach can lead to several complications:
- Delayed Feedback: When we deploy after months or even years, we miss out on valuable user feedback. If we’re not iterating, we’re not learning.
- Technical Debt Accumulation: Larger code changes often lead to more technical debt. If we’re not revisiting our code regularly, we’re likely to leave behind messy implementations that become harder to manage over time.
The Benefits of Frequent Deployments
On the flip side, frequent deployments can transform our development process for the better. Here’s how:
Smaller, More Manageable Changes: By breaking down our work into smaller, valuable increments, we can deploy more frequently. This not only makes our code easier to manage but also allows us to gather feedback sooner.
Increased Maintainability: When I know I’ll be editing my code again soon, I’m more inclined to write maintainable and adaptable code. The thought of my colleagues having to clean up after me adds a layer of accountability that encourages better practices.
Real-Time User Insights: Frequent deployments allow us to observe how users interact with our features. This data is invaluable for making informed decisions about future development. If a feature isn’t resonating with users, we can pivot quickly rather than investing time and resources into something that may not be worthwhile.
Confidence in Delivery: Regularly shipping code builds confidence—not just in our product but also in our team’s ability to deliver. When we see the impact of our changes in real-time, it reinforces our commitment to continuous improvement.
Embracing Hypothesis-Driven Engineering
Incorporating hypothesis-driven engineering practices can further enhance our approach. By collecting telemetry and data, we can validate whether we’re building the right features. This means we might choose to incur some technical debt initially, knowing we’ll revisit the code based on user feedback.
- Iterative Development: We can afford to do a quick job on the first slice of a feature, allowing us to test its viability before committing to further investment. This iterative approach not only reduces waste but also aligns our development efforts with actual user needs.
Practical Steps for Improvement
To truly reap the benefits of frequent deployments, consider these practical steps:
- Reduce Batch Size: Aim to make smaller changes that can be deployed quickly. This reduces the risk associated with larger changes and makes it easier to identify issues.
- Increase Deployment Frequency: Strive for more regular deployments, whether that’s daily, weekly, or bi-weekly. The more often you deploy, the more opportunities you have to learn and adapt.
- Enhance Observability: Invest in telemetry and monitoring tools to gain insights into how your code performs in production. This data will guide your future development efforts.
Conclusion
In my journey as a developer, I’ve learned that frequent deployments not only improve the quality of our code but also enhance our understanding of user needs. By embracing smaller batch sizes and increasing our deployment frequency, we can tackle technical debt more effectively and ultimately create happier customers.
So, if you’re looking to improve your development process, I encourage you to take a hard look at your deployment practices. You might just find that the key to better code and a more successful product lies in how often you’re willing to ship.
When we don’t deploy very frequently, there are a number of dysfunctional behaviours that don’t have to be there but tend to creep in when we’re deploying bigger chunks of work. One of those things is that we tackle bigger chunks of work instead of taking a big thing and breaking it down into smaller things. We try and tackle that big thing, and because we tackled that big thing and then we deploy it maybe after a couple of months, maybe after a year, that is unlikely to be edited. We’re not going back around and editing that code again and again and again. Maybe in five years’ time, somebody will come back around and add something to that feature.
But if we’re deploying more frequently, if we’re deploying faster, then our deployment window is smaller than the features that we’re delivering. So we’re thinking about how do I take this big chunk of work and break it down into smaller things that have value? If I’m deploying that smaller thing that has value, I’m probably going to be thinking about, well, if customers are using it, I can look at the data of how customers are using it and perhaps change the way I build it.
So that’s one advantage. But the main advantage from a technical debt perspective is that when I’m writing that code, I know that I’m going to have to go edit it tomorrow to add the next slice and perhaps edit it the day after that to add the slice after that. So I’m thinking more about how do I make sure that this code is maintainable, supportable, and adaptable because either I myself or one of my colleagues is going to be coming and editing it tomorrow.
You don’t want to, I guess there’s a little bit of peer pressure, right? You don’t want to look bad in front of your colleagues. I shipped some really bad code, and now my colleague has to go clean up my mess. That’s not polite, right? Because they’ve got to go edit it again. But even if I’m doing it myself, I’m editing it again tomorrow.
I don’t know if you’ve ever been in this position, but quite often when you find some nasty code, something that’s written very badly, and you want to go look up the blame tool to find out who do I blame, usually it’s yourself, right? You’re the one who made the mess, and you don’t remember. So even if you don’t want to see yourself that way as writing that messy code, then we take a little bit more care.
It’s also worth noting that if we’re doing hypothesis-driven engineering practices, right, so that we can collect telemetry and data and then validate whether we’re actually building the right thing, then we’re perhaps only going to invest in the second slice if it’s worth investing in it. If users are connecting to it, if they still desire to continue to invest in that feature.
So quite often, we actually want to do a quick job on the first slice. We’re choosing to incur technical debt, knowing that we’re going to be looking at the telemetry, looking at the outcome, and seeing whether it resonates with users and whether we continue to invest in it or we take that chunk back out.
So this frequent deployment can not only help developers tackle technical debt issues because it just makes it harder to incur technical debt. We make it easier to write good code because if you’re deploying to production at the end of the day, it needs to be production-ready. Every time I edit the code, it needs to be production-ready.
You’re potentially doing continuous delivery, right? Everything that makes it into main ends up in production. Every commit perhaps makes it into production. So reducing those gaps enables us to reduce technical debt because we know we have to do a better job. It enables us to understand our customers’ needs better because we can see them interacting with the partial thing we have built in order to get telemetry and feedback.
These small changes help build confidence, not only with the customer but build confidence in the product itself, build confidence in our ability to deliver and continuously deliver those small changes. So when the systems that we’re working on are big and complex and have lots of interactive parts, that’s when we want to be delivering smaller and smaller changes so we can see immediately when things go wrong.
Obviously, we need telemetry and we need observability of our system to be able to do that, but this is one of the core practical steps that any engineering team, any developer can take: make the batch size smaller. This works in a lot of cases, but in this particular case, if you’re delivering code to production, reduce the batch size, increase the frequency of delivery and deployment to production, and you will see an improvement in the code. You will see an improvement in the product, and hopefully, that will result in happier customers.