There is a way
We admitted above that what most teams want to improve are the number of features delivered, and the quality of the delivery (in number of bugs). But going for those directly means a passive, reactive stance. To reduce the bugs, you should have paid more attention, tested more exhaustively, or thought of this or that unintended consequence. To deliver more you should have coded faster. It is hard to have a plan to improve the situation from this point of view.
The other way to think about this is that the goal on the long run should be to do as well as the high performing teams. These teams are capable of several deployments per day, so we should advance towards that level of efficiency.
Deploying faster does not mean simply setting a faster deployment schedule and following it no matter what. That will no doubt end badly. Rather, we should analyze our process, detect the bottlenecks, and remove them iteratively, so that the team naturally feels more confident and comfortable increasing the speed.
These bottlenecks will suggest the next steps that need to be taken to allow the acceleration. The following is an enumeration, undoubtedly incomplete, of situations a team may be in, and techniques that may be used to solve them. The exact definition of these practices is out of the scope of this writing, but the hope is that they spark the curiosity of the reader. More information can be found in the references at the end.
- The user stories take too long to be completed: Making smaller User Stories. Slicing (dividing a big US into smaller chunks). Improving the architecture to make changes easier. Allow the deployment to production of code for features not yet finished (Branch by abstraction, Feature Toggling)
- The coders are often blocked waiting for reviews: Pair programming. Post-commit reviews.
- Many bugs appear in production and/or during the QA phase: More unit testing. Test Driven Development (TDD). Test automation in general. Tracing Bullets (minimal versions of a feature that touch all parts of the systems affected, so that all issues surface soon). Feature toggling (This allows practices like Dark launch, or canary launch, that make for safer release of new features, and also allows to turn off the features that are misbehaving, while the rest of the service continues to work).
- At the end of the period all features are integrated at the same time, causing lots of conflicts: Trunk-based development, or at least short-lived branches. Decoupling the merging of the code from the completion of a feature. Slicing. Pair programing and ensemble (a.k.a. Mob) programming.
- Product requirements change constantly: Feature Toggling. Slicing. Continuous Delivery (the head of the main branch is always ready to be deployed to production. See below).
- Bugs take too long to be resolved: Unit testing and Test Driven Development (TDD). Continuous Delivery.
- Deployments often end in failure: Deployment pipeline automation
- Deployments require a lot of extra work: Deployment pipeline automation. Automatic documentation.
Introducing these techniques is neither easy nor free. Some of these practices are difficult skills and it will take time and effort to acquire them. It may be needed to have some training or ask an experienced developer to join the team for a while. Other practices require serious investment of time and work in improving the internal tools of the team.
We have not mentioned the much popular practices of CI/CD, which can be defined as follows:
CI: Continuous integration. The practice of integrating code in the main branch of the repo at least once a day, each commit triggering a build of the code and a suite of automatic tests.
CD: This may mean Continuous Deployment or Continuous Delivery (which actually is mentioned as one of the recommended practices), which are not the same thing. Continuous Deployment means that each commit is automatically deployed to production without manual intervention. Continuous Delivery is more relaxed and means that the code is at all times ready to be deployed on demand.
These two are more wide-ranging practices, which require many of the techniques mentioned before to work (automatic tests, automatic deployment pipeline, short lived branches), so they are not as well suited to a small-steps approach. However, they may be considered as an intermediate milestone or a more general requisite to reach faster deployments.
Also, we failed to mention many other basic good practices that developers need to follow even before considering the recommended techniques, which we consider already accepted practices in the current coding scene, and necessary for a better development experience in general as well as a faster deployment pace. Some of these are code versioning using tools such as git, multiple environments (development and production, for example), team-wide coding conventions, etc.