I wrote this some years ago, mainly aimed at our java devs, but I think it comes close to my personal manifesto for coding in general.
1. Small Pieces, Loosely Connected.
In order to attain the sort of performance we need from (our product), and in order to be in a position to respond quickly and with agility to changes in the business, we cannot build and deploy monoliths. Our architecture and performance design is predicated on being able to scale out the service by horizontally scaling pretty well all components. Each component should be self contained, must not be dependent on the internal state or implementation of another component, and be able to be deployed into the smallest and simplest run-time environment possible. Assume we will be deploying dozens of instances of any component. Assume that we may have different versions of the same component running. Assume that we will be doing continuous deployment as well as continuous integration.
Separation of concerns is a concept sometimes applied to the construction of code, but seldom effectively applied to the construction of entire systems. We should build components that provide very specific and easily defined services, and that aim should be carried down through the layers all the way to objects. If at any point we describe a module or component using â€andâ€, we have a candidate for decomposition.
2. Keep it Simple. Small is Beautiful.
Small components are more easily maintained, tested, and modified. Large things are more fragile. Aim for things that can be described on a napkin, and designed on a whiteboard.
3. Test Everything.
Test your code. Test your designs. Test your implementation plans.
Test your assumptions and beliefs and habits.
Build test harnesses, unit tests, integration tests, performance tests. Play in sandboxes.
4. No Broken Windows.
All code contains bugs, and coding is the art of writing bugs. That is no excuse for not fixing bugs. Addressing the small, unimportant, niggling inconveniences wherever possible is as valuable as pushing out new features: every broken window fixed improves the quality of the product. Spending the time to fix a time-consuming annoyance saves time in the long run.
Don’t check in code that breaks the build. Let me repeat that. Don’t check in code that breaks the build.
5. Thread Safety Matters.
Assume that any data, including private member data, can be simultaneously accessed by or modified by multiple threads. Always consider the implications of that. Don’t be afraid to use the thread safety tools provided in the language, and don’t assume that state violations only occur under high concurrency conditions.
6. Optimise When You Need To.
The system as a whole has aggressive performance targets. That does not mean that all components have the same performance requirements. Don’t waste time in premature optimisation: remove performance bottlenecks that are found in testing. Profile your code.
On the other hand: assume crossing boundaries is expensive. The cheapest and quickest access is in local memory. Reading from disk, from a database, from a remote web service can be assumed to be an order of magnitude or more slower than memory access. The implication is: cross boundaries as little as possible.
7. Finish the Job.
You are not here to write code and tests. You are here to design, to communicate, to teach, to explore, to deploy, implement, refactor and attempt to destroy. Ask yourself, every time, what the relevant definition of “done” is. And when you are done, wipe down the benches, sweep the floor and put away the tools.
8. Boldly Refactor.
Go read the canonical books, then come back with a chainsaw. If you are scared to refactor because you don’t know what will break, your tests are inadequate. If it does break, you can always revert the code to the last checked in version.
9. Today is a Good Day to Die.
Work as though you may fall under a bus at lunchtime. Your code will have deficiencies that need to be corrected, and will be modified when requirements change. This means that somebody else, possibly a future you, will read and modify code. Make it easy for them, and strive to ensure they won’t curse your name.
10. Plan for Failure
You know what your code is supposed to do: build tests that try to break it. Then go back and stop it breaking. Assume the callers of your code will pass rubbish in, and horribly misuse it. Program accordingly. Assume if you call out to some other system or module that the call will fail, and that you will get rubbish back. Program accordingly. The disk will fill, the database will explode, and the server will crash. Deal with it.
This is doubly true of anything going out across the Internet, or a WAN: remote service calls can be relied on to fail. Deal with it.