I wanna share a principle, based on my experience of collaborating with non-PLSE background programmers in building many small but critical production systems.

This principle is called incremental software engineering:

  1. Avoid pre-matured optimization at all cost. Simplicity is the most important thing since we need to deliver things & integration-test them as soon as possible. Also, programmers come and go. You don’t want them to write a fast but cryptic prototype and then leave. Start things simple, and then optimize it incrementally to guarantee its correctness by unit-tests and regression tests.
  2. Scale your architecture incrementally based on your actual workload. Even though Docker and Kubernetes are the hot things today, they are not meant for you if you only have a prototype and a few customers. These fancy “cloud-native” architecture will make your slow in developing your business logic. The first priority is to be agile and be correct, not to be fancy. For example, why not just start a single machine, a single process, and a single thread with everything stored in MySQL first? This setup also makes early-stage continuous-integration affordable. After that, observe the real bottleneck in your system, which depends on your actual workload. Then upgrade your architecture design to attack this bottleneck. Leave others non-bottleneck part intact.
  3. Introduce automated checks without interfering the mainline of story. Automatic checks are cool, and effective in helping us prevent silly mistakes as well as severe bugs. They include all kinds of fancy style-checker, linters, type annotations, bug detector, etc. However, setting them up and making them happy are costly. Don’t try to add them all in the early stage of your development, esp. for non-stateful code. When you add these infra later, do it one at a time, and make sure that it won’t break the existing code and CI system severely. Don’t make programmers churn. Instead, adding stuff incrementally: like add an extra check flag once a week, only performing check over the diff of the code, rather than the entire codebase etc.
  4. Optimize your dev-flow based on your need. Should we use staging model for a project? Should we use stable branch and master branch, or release on master? Should we use version tags? Should we cherry-pick to master, or merge to master? Should we have feature branches? ….. There are 100000 questions like this, and it is impossible to answer them in the early stage of your project — in fact, you might not be able to fix this in any stage of your project, until it dies. So, the right solution is to discuss with your teammates if the current model works well when you feel less productive, and figure out a small, incremental change to make the dev-flow better fit our current need. The ultimate goal is to deliver softwares that make the entire product a success.

There are other possible aspects, but I will leave them to your readers. As you may conclude, behind this principle of incremental software engineering, is the philosophical view that, nothing is perfect, esp. in a system. The value of the system doesn’t happen after it is built, it happens while it is being built, even while it is being defined. Accept the imperfections and chaos, but have a sharp eye for optimizing your ROI (return-on-investment).