- Being able to CMD-click through code >> any other theoretical concerns.
- CMD-clickability is the opposite of indirection - how does this function work? CMD-click it.
- Non-CMD-clickable codebases are filled with non-meaningful abstractions and tend to be longer.
- Static analysis tooling should be easy to write.
- Stack traces should correspond with the structure of the code.
- Higher order functions break the golden property, use them as a last resort.
def do_something(data, f: Callable[...]):
f(data) # I can't CMD-click f
- Trad Object Orientated code tends to break the golden property (this makes sense as objects are closures), use it as a last resort.
def do_something(a: Animal):
a.meow() # I can't CMD-click meow
This case it is ambiguous as meow()
could be on any Cat
, Lion
, Tiger
...
- Use enum-like information to distinguish between otherwise similar data.
class Cat(Animal): ... # bad
a = Animal(kind=AnimalKind.CAT, ...) # good
- Serializable enum-like discriminants in data can be trivially plonked into databases, regurgitated via JSON, etc.
- Choose behaviour based on data, not class hierarchies:
def do_something(data):
if data.kind == Kind.A:
g(data) # I can CMD-click g
- Say arbitrarily complicated things about data, rather than be constrained by inflexible heirarchies.
class Cat(Animal, FourLegged): ... # bad
def has_even_number_of_legs(a: AnimalKind) -> bool # good
- Strong typing aids CMD-clickability, this property of typing is more valuable than correctness.
- Microservices in and of themselves are not bad, however:
- Construct your codebase such that CMD-clicking across a service boundary is as easy as within a service.
- Typecheck service boundaries as you would any other code.
- Use correlation ids and a Datadog-like tool to make cross-service stack traces comprehensible for debugging production.
- Where you might have to fall back on grepping - make it easy, use full literals:
@app.route(PREFIX + "/v1/add") # bad
@app.route("/payments/inbound/v1/add") # good
- Good code expresses the smallest possible state space the program could operate in - YAGNI.
def do_something(data, f: Callable[...]):
# f could be anything - big state space
f(data)
def do_something(data, kind: Kind):
# we enumerate the specific things we can do - small state space
if kind == Kind.A:
g(data)
...
- Lean in on language features, don't introduce unnecessary abstractions.
- There exists deep library code where we want to allow consumers to do anything (extensibility). In application development, this is the exception - you have control over the whole codebase, the code by its nature is extensible.
- Ignore this manifesto sooner than code anything outright barbarous.