A lot is written about best practices around DevOps and the following guide will highlight some practices that will further enhance your development team's productivity and happiness.
Environments to Fit the Purpose
One of the mantras of having pre-production environments is that they should be identical to the production environment. Obviously, you do not want to test your software against a different version of MongoDB in Staging vs Prod. It is, however, important to recognize that each environment is tuned for a particular engineering outcome. Staging environments can be useful for demos and tests that are difficult to automate and load-testing environments are about how your application performs under load. They need not blindly match Production. A Development Environment is one where developers who are working on different parts of an application can collaborate and test interactions between the services they are working on. This can be a change to a backend API that requires a corresponding frontend UI change, or two backend services undergoing a schema or service contract change. It is important to tailor these environments for the purpose they serve so engineers can focus on the aspect of software development, rather than being a puritan about having identical environments.
Many Automatically Provisioned Environments
Of course, if a team is going to have these different environments for specific purposes, one shouldn't assume there's only one instance. We routinely spin up development environments that are tied to feature branches. At any given time, we may have several features under development. It is best to empower your engineers so they can spin up environments themselves without having to ask for help from a dedicated infrastructure expert. Your infrastructure as code setup is a good place to see the degree of differences between the various environment flavors. For instance, you may have an ephemeral in-cluster database for development and a managed version of that same database in load test and production environments.
Separate Credentials from Configuration
One of the main challenges to having many disposable environments is managing all the various configurations. One common mistake we see is combining configuration with credentials. Configuration includes things like service names and teams should strive to use the same configuration across all environments. If your database in Develop is postgres-db:5432 then it should also be postgres-db:5432 in production. A great trick is to use Kubernetes headless services to alias service names so postgres-db:5432 in production points to your PaaS PostgresDb for instance. That way, your development team can seamlessly switch between environments without encountering a configuration nightmare. The only thing one would then have to deal with is switching out the credentials which should be managed in a secure credential store. These stores typically have the ability to switch context and roles and this can be leveraged to make the move from one environment to another seemlessly.
Locally Attached Collaborative Environments
Once you have the above setup, it's really easy to use a tool like CodeZero because it extends your local development into any of these environments. CodeZero makes it so your local development machine becomes one with any of your environments - including production if required. This makes it really easy for your developers to use first-class tools like debuggers and IDEs to diagnose, build and make changes to the software. Using environments in conjunction with your local workstation in this way allow team members to make changes and test with unprecedented speed. As this becomes part of the engineering culture, teams have the agility to move from one feature environment to another and even work on issues in production during outages and other incidents if required.
Emphasize coverage over speed in your CI/CD Pipeline
When your team is freed from having to deploy in order to collaborate and test, there isn't as much pressure on reducing the time taken up by your CI/CD pipeline. You may be tempted to cut out automated integration tests or really long-running automated UI tests in order to reduce deployment times. The pressure on deployment times is generally because engineers want to quickly test a subset of the application without having to wait tens of minutes for the deployment to go through. This can cause teams to start cutting corners in their testing automation. With real-time local collaborative development, you don't have to wait around for a deployment in order to check that a feature works as intended.