Move Slow and Fix Things

Mark Zuckerberg coined the term “move fast and break things” and ever since, developers have been plagued by expectations to release half-baked solutions for the benefit of product owners’ experiments.

The obvious downside of this approach is low-quality solutions running in production. Experiments that were meant to be “temporary” tend to stay online if they prove successful. Products accumulate technical debt due to  fast and temporary development, leading to high maintenance costs, longer release cycles, and more often than not complete rewrites.

Not that it’s all bad. I believe that “move fast and break things” played a significant role in the rise of DevOps. The incompetence of Mark Zuckerberg and other product owners forced us to rapidly evolve and adapt our ways of working to meet those expectations. Shift left and automate all the things.

Now we’re here again.

Product owners experimenting with Claude Code over the Christmas holidays are now demanding that we ship AI slop into production systems in pursuit of promised 10x productivity gains. We’re not ready, and the result will be very similar to “move fast and break things”. We’ll end up with broken, unmaintainable products running in production, riddled with bugs and security vulnerabilities.

We might find new processes and tools to guard against the decline in code quality, but I hope not.

I believe the real problem is that we’ve lost sight of good product management in favor of chasing the latest hype. Nobody wants your AI agent in their favorite software. We should return to purposeful product design – where we move slow and fix things. Let your product be known for its stability, for working year after year without bugs and for consistently delivering features that users really want.

I call for not moving fast and not breaking things. Not for chasing 10x productivity gains at the expense of stability, security and maintainability. I call for finding the core of your product and improving it incrementally, evolution instead of revolution.

I call for moving slow, and fixing things.

Rigor Mortis

Disclaimer.

This blog post is about the overuse of code quality methods. If you’re not using good practices for code quality, the advice against overuse doesn’t apply to you. Never stop doing what you never did, because that someone on the internet has overdone it and believe that they should not do it as much anymore.

What is Code Quality

Software Quality is a very wide concept, but when it comes to code quality I find it quite easy to pin down. The following aspects are often mentioned in regards to code quality

  • Low cyclomatic complexity
  • Easy to read
  • Easy to test
  • Can be reused
  • No side effects

These aspects comes down to making the code easy to change. That is why my definition of high quality code is, code that is easy to change.

Code Smell: Rigor Mortis

Code can be easy to read, well tested, documented, reviewed and still be hard to change. Code quality practices can work in a way that locks down the code and makes it harder to change.

If you strive for your code to be perfect, it will also become hard to change. Once you try to change it, tests will break, code quality tools will complain about loose ends and the compiler wants you to fix 20 compilation errors.

I call this code smell Rigor Mortis.

Too Much Quality Slows you Down

If you apply too much quality methods, the code will become harder to change. It will look great, but if it cannot be changed it is dead. Businesses that depends on software being easy to change, will be impeded by code too rigid to change.

Unit Testing

The practice of writing unit tests is a quick way to increase quality. Done the right way it will help you refactor a program and introduce changes while keeping track of unintended side effects.

Too many tests, or tests written in the wrong way, will make the program harder to change.

  • Tests fail when they have high coupling to the implementation of the system under test. Those tests are brittle.
  • Tests fail when you alter the behavior of a program. These tests were intended to fail.

Tests that fail require work to fix them. Each failing test makes it harder to change the code. Good tests only fail when you break your program, and all other tests makes your code smell like rigor mortis.

Static Typing

In compiled languages like C# and Typescript the compiler will check your program for errors. This provides quick feedback of syntax errors, spelling errors or logical errors. It helps you code faster.

Static Typing will also slow you down. The compiler can be so strict that you spend more time trying to satisfy it than making the desired change of your program.

Making a change in a statically typed program, may require you to update code and models in 10 different places to satisfy the compiler. If this helps you prevent errors, that is a good practice, but if it only slows you down it smells of rigor mortis. 

Linting & Static Code Analysis

Tools that automatically review your code may help greatly in avoiding common mistakes that could take hours to troubleshoot. They are very helpful in teaching us quirks of the language that we should watch out for.

Linting tools also works the other way around. It will complain and stop you from making unsafe changes that are needed in troubleshooting. Commenting out a line of code will lead you down a rabbit hole of making sure no references are unused, just to satisfy the linter.

Watch out when your linter makes your code harder to change. It smells like teen spirit, .. I mean rigor mortis.

Summary

I’ve introduced a code smell, that is the smell of too much quality, and I’ve given you some examples of when this smell applies.

All these are good code practices by today’s standards, and you should apply them to your projects. But you also need to beware so your quality methods doesn’t impede with your ability to change your code.

You don’t want your code to reach a state of rigor mortis.