This is a saying that makes the rounds every so often, and there’s a kernel of truth in it. It comes out of the military, and it has multiple meanings.
The first is that a group of people can only do something so fast without messing up. If you run as fast as you can, you’re more likely to fall. If you try to do something really quickly and skip steps, you’re more likely to make mistakes. In the military, mistakes can be really big problems. People can be hurt or die. Officers don’t like that. It looks bad on a report.
The second meaning is that you should practice slowly so that your motions become smooth. Then when you speed up your motions, they’ll still be smooth. This makes sense to me as a classical musician. It makes less sense as a software engineer, so we can safely ignore this definition for the rest of the blog post.
Usually when someone says “slow is smooth, smooth is fast”, they’re recommending caution or justifying taking the time to do something properly. Another similar saying is “haste makes waste” – the idea is that by ensuring something is done correctly you’ll avoid having to do it twice. In a lot of situations that’s good advice. Rushing to get code onto production and skipping steps in the normal process to save X amount of time will result in a Y% chance of a bug in production which will take Z amount of time to resolve. If X < Y * Z, the recommendation is correct, and “slow is smooth, smooth is fast.” However, that test does not always evaluate to true. For some changes, a full QA cycle or whatever the normal process is will take longer than just testing on production. In those cases, slow is just slow, and we should ship the thing without the extra fuss.
So, then, how are we to know the difference? We need to understand the level of risk we’re taking on in making the change, as well as the cost to remedy any bug that might be introduced. The level of risk is the likelihood that we’ll introduce a bug. The better understood, tested, etc. a piece of code is, the lower our risk of introducing an issue. The more legacy, crufty, convoluted, or complex the code is and the fewer automated safeguards we have in place generally the higher the level of risk becomes.
The cost to remedy represents the severity of issue we think we might cause. Some bugs are quite severe: Shipping a database migration that drops the wrong column (or table, God forbid!) is potentially disastrous, bad algorithmic trading code can bring a billion-dollar company to its knees (this example is from real life – Google it). In these cases the cost to remedy the issue can be very high, sometimes more than the organization can pay. Others bug scenarios are not so dangerous: a copy update on a static HTML page has limited capacity to cause harm, same with a tweak to a well-understood bit of application code that isn’t mission-critical. For the latter two, the resolution only involves fixing the code and pushing it to production, and that’s probably the end of it. A few minutes or an hour’s work at most.
As we try to make the right call about the level of risk and the cost to remedy, we also need to have a feel for our own level of certainty in our assessment of both. Are we missing something? Is that application code really as simple as it seems? Maybe it’s reused somewhere else and we’re going to create an unintended side-effect. Maybe that copy tweak is part of a contract. Maybe there’s some unknown unknown that will bite us. If we know the code like the back of our hand, we can be more certain of our assessments, but even then we can’t be totally sure. So, when in doubt (which is most of the time), falling back to “slow is smooth, smooth is fast” is the right call. But, if the risks are low, go ahead and skip that 3rd round of QA. You really don’t need it.