You need to be able to run your system

5 hours ago 2
When developing a system, it is important to be able to run the system in its entirety.

"Run the unit tests" doesn't count. The complexity of your system is in the interactions between the units.

"Run an individual service against mocks" doesn't count. A mock will rarely behave identically to the real dependency, and the behavior of the individual service will be unrealistic. You need to run the actual system.

"Run an individual service in a shared stateful development environment running all the other services" doesn't count. A shared development environment will be unreliable as it diverges more and more from the real system.

"Run most services in a mostly-isolated development environment, calling out to a few hard-to-run external services" doesn't count. Those few external services on the edge of the mostly-isolated development environment are often the most crucial ones; without the ability to run modified versions of them, your development process is hobbled. Furthermore, being dependent on external services greatly complicates where and how you can run the system; it's much harder to, for example, run tests with the system on every commit if that will access external services.

"Run all the services that make up the system in an isolated development environment" counts; it's the bare minimum requirement. Bonus points if this can be done completely on localhost, without using any off-host services.

Without the ability to actually run the entire system in this way while developing, many evil practices will tend to become common.

  • Testing is harder and far less representative, and therefore many issues can only be found when changes are deployed to production.
  • In turn, production deployment causes issues more often, and so deployment is slower and less frequent.
  • Users of components are reluctant to update to new versions; developers of components are reluctant to make changes. The system as a whole becomes ossified, with excessive emphasis on the maintenance of stable interfaces instead of improvements to the system as a whole.
  • Deploying the system to new environments is more difficult, since the developers aren't able to actually run the system. Existing practices in production will be cargo-culted and copied around indefinitely, even when they are unnecessary or actively harmful.
  • Exploratory usage of the system is very difficult, so it's harder to consider using the system for purposes outside what it was originally developed for, and new use cases become rare.
  • Downstream clients who depend on the system also suffer all these issues, since without the ability to run the upstream system in development, they can't run their own entire system, which is a superset of the upstream system.
Running the entire system during development is the first step to preventing these issues. Further steps include writing automated tests for the system (which can be run repeatedly during development), and using, as much as possible, the same code to run the system in development and in production.

Developers of large or legacy systems that cannot already be run in their entirety during development sometimes believe that it is impractical to run the entire system during development. They'll talk about the many dependencies of their system, how it requires careful configuration of a large number of hosts, or how it's too complex to get reliable behavior.

In my experience, this is often wrong. These systems can be run locally during development with a relatively small investment of effort. Typically, these systems are just not as complicated as they're thought to be; once the system's dependencies are actually known and understood rather than being cargo-culted or assumed, running the system, and all its dependencies, is straightforward.

When running your system, it can seem tricky to run your dependencies, especially if they're mostly services rather than libraries. Fortunately your dependencies have the same goal: They also need to be able to run their system! They shouldn't reserve that ability for themselves; they should pass it down to you, the user of their system. Likewise, you shouldn't reserve the ability to run your system for yourself; you should pass it down to your own users, so that they can run their system, which is a superset of yours.

Being able to run your entire system during development is just about the most basic requirement for a software project. It's not, on its own, sufficient for your development practices to be high quality; but if you can't do this, then you're not even in the running.

Read Entire Article