Thinking like a craftsperson

They are just your current best understanding of how to do something in a given context. As your understanding evolves, so should your practices.

The best way to have no bugs and ship fast is to have no code and ship nothing. Of course you're in the business of delivering value and it's difficult to deliver value with nothing, but you won't stray further into building more than is necessary to avoid building the wrong thing and lock your code (and your mental model) into something that should never have existed in the first place. You want to avoid wasting time by building something that will never be necessary. This is similar to the 'You Ain't Gonna Need It' (YAGNI) and Keep It Simple and Straightforward (KISS) principles.

This isn't just a fun saying you've heard before. This makes an actual impact on your ability to deliver the right amount of work to the right problems. Too often you can find yourself stuck in the enjoyment of optimizing the thing that should not exist. Why bother making the wrong thing work or making the thing that doesn't work fast?

Prioritize the ability to deliver real value and iterate rapidly while maintaining quality. Fast, reliable iterations enhance the user experience by enabling timely improvements and quick responses to feedback. Sustainable velocity is not just about speed; it's about building processes and codebases that support efficient, low-bug development over the lifetime of the project (however long or short that may be).

You don't know the future, but you can feel confident things will change and optimize for that. You avoid wastefully spending time on early abstractions and optimizations until you have more context and a clearer picture of what's needed. Prefer options which enable the greatest future flexibility.

You don't always need the most correct thing. Sometimes you need something that works now. Additionally, each app you build has unique constraints and requirements. You do your best to build the simplest solution that satisfies the requirements while ensuring you do not paint yourself into a corner. Even when it comes to these principles, some level of pragmatism is necessary in their application.

Consistency in a codebase helps keep things clear and predictable. Using four different patterns or libraries for accomplishing a single task just adds mental overhead. Strive to consolidate these things where it doesn't increase complexity and act with consistency as a team.

When evaluating something new, avoid prejudice simply because it's unfamiliar. Evaluate it on its own merits and see if it can earn a place in your mental model and potentially replace something that is more familiar.

Playing with new things is fun, but when your product code becomes the playground where you learn new things, bad patterns are unwittingly encoded and then naturally propagated over time. Often it's best to make your inexperienced mistakes in less important projects first.

Users don't know or care what tools and services are used to deliver the promised value. So you take ownership of the entire solution, even if some parts are provided by others (open source, third party services, etc). For this reason, it is important that each dependency you add justifies its value to the solution.

Linters, type checkers, and even formatters are incredibly powerful tools that can help guide our codebases and avoid runtime errors. They replace fine grained low level tests that nobody will write anyway. It can be easy to offload thinking to overly ambitious linting tools. These can cause you to write verbose code or do weird things just to satisfy them. A linting rule must justify its existence. Formatters save enormous amounts of time and arguments about silly things that don't matter. Use one and move on.

Occasionally when weighing pros and cons of different approaches to a feature or solution, you'll have issues with each implementation on the pros and cons list. An important factor when evaluating the cons is whether the problems are long term or temporary. Focus on solving long-term problems rather than temporary ones if the solution is intended to last longer than the temporary problems.