No Clever Code

Read this first

We Can Afford To Buy Vowels

TL;DR Programs must be written for people to read, and only incidentally for machines to execute —Harold Abelson

In the old days <eyeroll/>, we had 26 variables: the letters of the alphabet. The next leap in programming, we could add one digit after the letter. That was not good enough for development then, and it surely is not now

With no restriction on the length of names, the vestigial traditions of opaque naming cannot hide behind brevity or idiom, and certainly not consistency1. The only measure for names is their utility in communication

A line of code like r.Get(true) is opaque. While IDEs can help decode what the r and the true mean, using descriptive names allows the code to speak for itself: orderReader.Get(CacheDataOk)

Multi-letter abbreviations harken back to Hungarian notation: abjad2 type names prefixed or suffixed to a (presumably) descriptive name (e.g. submitBtn). Or...

Continue reading →


A Function’s Interface Is It’s Bond

TL;DR: A function must respect all arguments or return an error

The Arguments

The only realistic way a function can fail its interface is how it treats arguments as mis-typed arguments or return values are usually caught by a framework

All The Arguments

Be they required or optional arguments that are present, functions must respect all the arguments; they cannot ignore some because they are inconvenient or contradictory

“I’d like a number four with no cheese”

One cannot stop paying attention just because we know what a 4 is. Functions and especially REST endpoints often accept resource ids or primary key values, which implicitly identify specific resources. Tempting as this easy lookup is, the function must apply all the arguments even if they are additional criteria. With a key value in hand, the function does not need additional criteria, but it cannot ignore them

A function...

Continue reading →


Walk, Don’t Race

TL;DR

Don't Race.png

Cha-Cha-Changes

Breaking changes happen, and the breaking occurs between the layers (e.g. UI and API, API and database). Rather than trying to release new layers simultaneously (especially with edge distribution), commit the time and effort to a four or five-step pattern that ensures the layers work perfectly at all times, losing no data

1. Create the Change You Want To Be

Create the goal: the new schema; the new endpoint; the new protocol. Test it, benchmark it, and be happy with it. Copy and translate the old system’s data to the new system (handle the subsequent data changes in step 4)
For example, instead of changing the data type of a column, create a new column of the desired type

2. Embrace the New While Respecting the Old

The Problem With Data

It’s always the data

The transition depends on how the old and new systems share data

  • Type A: if the clients are making...

Continue reading →


You Don’t Know Why I’ve Called You

An advantage of functions (or methods or subroutines) is boxing up a part of a process so it does not clutter up the code that calls it. Like telling one’s spouse to “take out the trash”, it reduces all an operation’s steps into a single name. The code in a function needs only to perform a single task1 according to all the arguments the caller passed in2 or signalling the caller of an error

As the function does not know who called it or why, it does not have the context to know how to react to errors. It does not know if it should log them (and if so, the logging level), retry an operation, terminate the entire process, or ignore the error. The only thing it can do is notify its caller of the error. The caller might have enough information to know what to do; if it does not, it passes the error to its caller

By acknowledging the limitations of its tiny perspective, a function does not...

Continue reading →


A Simple Caching Pattern in Go

One might use it like this:

var myCache = NewOurCache(0, calculateTimestamp)
// ...
var myValue time.Time = myCache.Open(dateText, timeText, Location)

OurCacheType is an alias for the actual data cached. It can be any type including a structure or pointer. composeKey() is an example how to consistently create a string key from whatever identifies the data. These parameters usually match the parameters of the createAValue function (if used)

Use the cache by Write() values if the Read() cannot find them, or you can use something like the Open() function, which returns the cached value if found in the cache or it generates the value (using the createAValue function), caches it, and returns it1

If a cache is disabled, Read() always returns found == false (even if it has the desired key already in data) and Write() does nothing. Open() will call the generation function every time

import
...

Continue reading →


Single Responsibility Lines‽

From Wikipedia:

The Single Responsibility Principle (SRP) is a computer programming idea: every module or class should have responsibility for a single functionality. All its services should be narrowly aligned with that responsibility. I leave the discussion about SRP in modules and classes to the plethora of sources addressing them

Yeah, But Every Line

Yes, every line should perform a single1 task. Even if you are using Perl, every line should have one specific goal. Complex expressions are ¡very impressive¡, but such preening makes the code hard to understand2. Breaking complex expressions and commands into informational variables and steps makes them easier to read3, easier to debug, and easier to refactor

There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated...

Continue reading →


Don’t Worry About Anything, Just Call Us

Callbacks are great and always have been. Long ago, we didn’t know we were injecting control or inverting dependencies; we just used them. When closures appeared, I did not see a huge difference: a little state here, a little context there. I have found my Closures Killer App: remote cleanup

Here is an example in Go:

In the calling code (with dependencies on DataDog, Redis, etc.)

type cleanupSignature  func(aName, aType string) func()

cleanupFactory := func(aName, aType string) func() {
    if _, found := SpanFromContext(context); !found {
        span, _ := datadog.SpanFromContext(context, aName, aType)
        return func() {
            span.Finish()
        }
    } else {
        return func(){}
    }
}

baz := foo(cleanupFactory, bar)

// ...

In the called code (with no dependencies on context, DataDog, etc.)

func foo(cleanupFactory cleanupSignature, zap int) string {
...

Continue reading →


Don’t Mock It: Hack It

Python holds a deep, dark secret: it can be as unpredictable as Ruby if we let it. Even with the new typing1, the staggering flexibility of Python at runtime remains. We can change individual instances of classes to anything we want, and nothing can stop us

But It Would Be Wrong

The whole point of classes and instances is that they are predictable: every instance should act the same; every subclass should do the same things, perhaps in slightly different ways2. Cleverly3 changing instance 47 to get around some restrictive class design might feel good, but it is a land-mine you planted waiting for the next developer to come along

Except

Except for testing. Testing less than end-to-end involves making some code think that it’s in the real world when it is not. Be it mocks,, stubs, composition, or references to services, tests need to simulate other code to simplify, isolate, and...

Continue reading →


The Magical, Full‑Stack Developer or The Intellectual Shmoo

Software, at least traditionally, was made in layers and parts, and each part has its own domain and its own technology. Developers know multiple languages and multiple domains, i.e. their skills gained from training and experience, but no developer knows every language and domain, however inconvenient this is for managers, and therefore recruiters. A team would have members who brought different skills and experiences.
A recent innovation is the invention of the Full‑Stack Developer to get exactly the skills needed in one engineer

“Stack” is a description of technologies that interact with each other. Originally, the stack extended down to the operating system and the binary network protocol and the physical electronic connections, and now the most common range of the stack is the data store (e.g. Redis, SQL) to servers (e.g. APIs) using communications protocols (e.g. REST, Thrift...

Continue reading →


The Futures of Python

Futures defer execution of code. They can allow code to wait for external resources like file reads, network calls, and database access, and then automatically continue without stopping every other operation. They allow the definition (i.e. coding) of entire sequences of work in one place rather than having one function hand-off to another (presumably coordinated) function

Python’s syntax for Futures (and coroutines) is simple and, for me, surprisingly confusing at first. I have concluded that Futures in Python boil down to three (3) rules:

  1. Functions prefixed with async return a Future1 when called even if the function itself does not return a result. The code inside the function does not execute until something resolves the Future

  2. The ways to resolve a Future are

    • await <Future>
    • asyncio.get_event_loop().run_until_complete(<Future>) or ayncio.run(<Future>)2
  3. Use await inside asyn...

Continue reading →