Just to unpack the “CD” portion. There are two kinds of “D”: Delivery and Deployment. I like to differentiate between these when working with clients. I’m a CD coach (both kinds).
In continuous delivery, you typically have gate keeping which would use some kind of branching strategy like git-flow. This approach is typically gated through UAT environments and such. It’s common to map branches to environments in this approach, and it often forces you into code freezes and merge issues.
In continuous deployment, every commit can theoretically go straight to production. In reality however, teams still do some level of testing, it just doesn’t happen on the main branch and is therefore non blocking. If you can have each feature/bug fix be developed, tested and deployed independently, then you can have a continuous flow of deployments.
To achieve both low latency and a continuous flow of deployments, you would need to employ 2 techniques.
The first is to have a production-like test harness (a cut down version of prod with all external dependencies faked) that gets deployed in each feature branch. This test harness allows your QA team to perform verification and exploratory testing of isolated changes in a non-prod-like environment. This gives you enough (not total) confidence to progress code to production. You can complicate your test harness accordingly to increase your confidence by improving the fakes and making use of contract testing.
This technique would be used for feature that would take a max of 1 day of testing. Any more than that you risk long lived branches which leads to merge issues, so you need to use…
The second technique, which is to use Feature Flags for things that need a longer time to develop & test with QA, UAT and alpha/beta customers. You would set the FF to only expose the features to the required groups of people. In effect, what you’re doing here is partitioning a single environment with a single data store to be used for PROD, UAT and QA. The advantage to doing this is that you don’t have long-lived branches in the code. And you also get a fully integrated environment to test in.
The challenge in the second technique is to slice the features in the first place in a way where they can be incrementally delivered.
Note that without sufficient automated test coverage it’s futile to even attempt continuous deployment. It will be a constant struggle of regression defects entering the system as the code becomes more complex.
I saw some other responses referring to blue/green. This is a good technique as you can choose when to release a batch of work to production, however it’s forcing a batch-release approach into your system which is ultimately blocking deployments and is therefore not continuous.
Hope that answers your question.