Mindful programming rules
Finally I decided to share my rules for writing code. These are for your sanity, my sanity, and are hardly creative or innovative. Each of these rules you probably invented on your own, probably formulated in a different manner, or perhaps you read about somewhere in the internet. Every rule here can be replaced just by common sense. Order does not matter – I will keep numbering constant (I hope).
- First the
INTERFACE
s. It’s not an abstract class, not a keyword – more likeAPI
, spine, top, explanation how your implementation works - Keep your
IF
s as close to the top as possible, if not higher - Avoid
IF
s in implementation INTERFACE
selects correct algorithm variant- It’s better to copy&paste than make your function unreadable by
IF
s - Separate your
IF
s from The Implementation - Use
IF
s if you really must /
Keep code branch-less SWITCH
is a form ofIF
sTAG
your parameters /
Compiler does not read names- Name your parameters
- Use
STRUCT
for large number of parameters /
Group you parameters intoSTRUCT
- Don’t create
boolean
parameters – create functions assuming parameter’s name and value - Don’t store
boolean
members in class/object - Don’t inherit /
Why don’t you inherit from vector - Don’t create classes
- Don’t create objects
SOLID
is always relevant- Use names everyone understands
- Controller/Manager/Listener/Provider/Cloud/Sometimes/Magic/… – it’s another way of saying “I DON’T KNOW”
- Different purpose – Different name
- Set of overloaded functions treat as one entity
- Use names for terms that are intuitive to your team and your users /
A vector is a vector - Inheritance relation is never
IS
/
All inheritance diagrams from school are wrong /
When you inherit from a vector it’s not a vector any more - Remember what magic is (implicit)
THIS
,VTABLE
- Make your
IF
s as readable as possible - Class field or object field are still global variables, just of different degree
- Singletons are pure evil /
Global variables are evil - Remembering
INTERFACE
s is all you need - Function signature is an
INTERFACE
STRUCT
isINTERFACE
- Don’t partially construct – always in full
- Use preconditions, postconditions to verify your assumptions
- Function always assumes something
- Put in parameters only what you need and not the trash bin
- Make implicit what is intuitive /
Matrix multiplication is intuitive - Lack of knowledge is not intuitive – it’s a reason to educate
- Fix name of your functions after every change
- Primary concern of program using threads are the threads
- Primary concern of using threads is concurrency – threads’ cooperation
- Use
coroutines
/
Avoid callback hell - If you can’t use
coroutines
useReactive Extensions
orTransducers
- Don’t use threads
- Always remember the time, place and reason when you break the rules
- Put a link to Stack Overflow in the comment as documentation
- When not sure
ABORT
- When you keep casting class hierarchies you made an error
- Dijkstra tells us there are no bugs – just errors of programmer
- When done your creation should not
ABORT
- Preconditions, postconditions are for developers /
Developer is only interested inABORT
s - When in doubt use reification
- Log changes to your state /
Log your inputs - Prepare your state for serialization
- Serialize your state to log file every now and then
- Make your function as meaningful as possible
- Don’t scatter your algorithm like farmer does with the fertilizer
- Make your function independent
- Segregate your functions
- Segregate your functions after every change
- Don’t deduplicate your code with
IF
s /
Premature deduplications is source of all evil - “a” and “b” are meaningful names if it’s a mathematical equation
- Put a link to wikipedia in the comment as a documentation
- Don’t use long names /
Don’t call me by all my names every time - If unsure don’t catch exception
- It’s better to
ABORT
than spend two weeks debugging - Log all decisions
- Reify your decisions
- Don’t fix code – remove it
- Best code is the code you don’t have to see or debug to know how it works
- If you’re debugging you made a serious error /
Don’t debug - An
IF
inside matches theIF
outside – removeIF
inside - Where are the Unit Tests?
- Your edge case is my normal use case
- Don’t log “this should not happen” /
Every branch of your code will eventually be executed - Singleton may hide in every line of code
- Don’t mutate in getters
- Don’t notify in getters
- Hungarian notation sucks
- IHate
INTERFACE
s (as in keyword in Java, abstract class) - AppHolder is pronounced as Abhor There
- Think in tables /
Use vector - Blame the Singleton
- Blame Magic /
Blame Controller /
Blame Provider /
Blame Listener - Callable is a good
INTERFACE
INTERFACE
must be minimalTDD
stands for thought driven development- If you can’t understand the code you can’t fix it
- Separate you concerns /
Divide and conquer - Simplify your logic
- Every developer makes an error in
boolean
expression /
Keepboolean
expressions as simple as possible - Make your logs parse-able and readable
- Push unique
CORRELATOR
through every layer - Log
CORRELATOR
with your input - Fix database content after exception is thrown
- Don’t write code that is not exception-safe
- Don’t assume there are rules that user would not break
- Date and time are hard subjects – use good library /
Listen to Howard Hinnant - Early exit is better than several indentations
- Name your
boolean
s - Separate decision making from implementation /
First make a decision then act on it /
Don’t mix decisions and actions - Filter out null as soon as possible
- Write loops as if it was a filter
boolean
is not bit-wide- Always initialize your variables /
UseIILE
- Compiler does not read your comments, most users don’t either
- Make your comments as meaningful and short as possible
- Always express graphically your
boolean
expressions /
Unroll yourboolean
expressions intoIF
s ladder - Write your function as if it was a filter
- Don’t use inheritance when implementing concurrency pattern /
Concurrency does not mix well with inheritance - Pass different callback instead of creating
IF
s - When you don’t understand the code rewrite it for yourself
- Use
INTERFACE
s on every layer REDUX
is a beautiful pattern in every language- Even authors of Design Patterns want to get rid of Singleton
- Whole algorithm in only one file
- Finite State Machine is better than anything else
- If you can’t use
coroutines
orReactive Extensions
useDuff’s device
- Always try to find an algorithm for your raw loop
- Good code can only be written in iterations
- Make your code better every time you look at it
- Almost-Singleton is worst than Singleton
- There is only finite amount of decisions your code can make – make them visible to the programmer
- Code is not set in stone – experiment with it
- Make your assumptions part of
INTERFACE
- After creating
INTERFACE
create the implementation - Fix your
INTERFACE
s frequently - Make your dependencies explicit
- Write your method like transaction (
fetch -> modify -> commit
) /
Object’s fields may change in unexpected ways during execution of the method - Write your function as transaction that can fail
CONFIG
is aSTRUCT
– cannot be abstract or contain methods /
CONFIG
already established everything there was to establish- Create a separate callback for every phase of reading from
SOCKET
- Optimize things that happen often – like null checks, loop internals
TIMEOUT
does not solve your problems – it postpones your problems and creates new problems- Places where threads are started and stopped are synchronization points
- Don’t detach threads that you want to stop
- Thread managed cannot modify its managing condition
- Detaching thread detaches all its resources
- There is entire elephant in the room between the fact that
ABORT
MAY happen in production and the fact that it HAPPENED in production BUILDER
serves same purpose asCONFIG
passed to constructor- Illustrate usage logic of implementation in the
INTERFACE
- If you can’t make implementation meet
INTERFACE
expectations introduce another layer - Don’t let error handling be swallowed by accidental complexity – errors are part of the
INTERFACE
- Choice made in top layers should be propagated into implementation in the form of functions’ names
- State of TCP connection/thread/service/network/request is not instantaneously observable /
No sense in asking about current state of TCP connection/thread/service/network/request before queuing work on it /
Longer you’re reacting to the state of TCP connection/thread/service/network/request the less you know about the state - Object is observable if you can react to informations about it’s state. Object is instantaneously observable if you can stop the world while reacting to information about it’s state.
- TCP connection does not guarantee that your message was received - implement confirmation in your protocol
- Notify asynchronously about result from your TCP connection/thread/service/network/request
- Clean-up your exceptions and errors before crossing boundaries
- When faced with non trivial inheritance tree people tend to panic and revert back to defence programming - this in turn causes avalanche of problems that can only be solved by rewriting major part of the application
- If you inherit from class named “CommonSomething” modify it’s methods to be static and remove inheritance
- If you really have to make inheritance hierarchy make sure that method calling goes one way - up or down
LAYERS
may be relative on outside but must always be absolute in immediate neighbourhood- Path in inheritance tree is a set of
LAYERS
- Always respect directionality of
LAYERS
- Every aspect of your code should resemble Direct Acyclic Graph/
DAG
/
Functions and calls between them should formDAG
/
Objects and calls between them should formDAG
/
Classes and calls between them should formDAG
/
Packages and calls between them should formDAG
/
Modules and calls between them should formDAG
/
LAYERS
and calls between them should formDAG
/
Services and calls between them should formDAG
/
Dependencies between modules should formDAG
- When iterating over something complicated create an
ITERATOR
- Always include tests for failures of file system /
Don’t assume file system operations always work - Inform about a changes using diff and new state
- Imagine that your every function is part of closure of asynchronous task, called from inside some parallel queue,
on some super multicore machine, after unspecified period of time, in undetermined order relative to other tasks,
with parameters frozen, and in very dynamic and fast environment /
Remember Command Pattern - Inter-component orchestration cannot be hidden inside components being orchestrated
- Name your functions/objects/classes after the choice you make