Wanted: state machines in the language

Our programming languages are often structured around the problem domain of compilers and the archaic (for most of us) task of converting things people understand to a thing the computer can execute.

Why don’t our languages have deeper support for the ways we reason about problem domains or the ways we struggle to reason. For example, why aren’t state machines and checking their sanity (or marking their unsoundness) a thing in pretty much any language?

The unhelpful answer is “because you can write a state machine in library code”. Which leads me to ask, why don’t we have popular state machine clones? Why is there no xUnit or Sinatra of state machines that is widely cloned to fresh and exciting languages?

The cynical answer is “because many programmers don’t want to think that hard”. The optimistic answer is that there’s room for someone to capture this problem space as well as xUnit did for programmer testing or Sinatra did for turning URL-like strings into method calls. You could be famous!

We’re all adults here, but we’re not all mind readers

My favorite advice on the topic of method visibility (i.e. public vs. private) comes from Python creator Guido van Rossum. It goes something like “we’re all adults here” and says it’s not really a necessary thing for compilers/runtimes to hide methods from specific callers. Don’t go mucking around in other object’s implementations. I still think that’s mostly right.

Except, coming up to speed on a new code base is vastly easier when there’s some delineation of the waterline between an object’s public API which it expects other objects to use and its private implementation which it does not. It tells me a) the private methods are open for change and refactoring and b) below the private “waterline”, don’t bother going any deeper when spelunking to figure out how this program works.

In a new or strange codebase, every little bit of tractability helps.

Here comes GraphQL

GraphQL is gaining purchase outside of the JavaScript communities and this seems like a pretty good thing. Shopify and GitHub have jumped on board. Absinthe (Elixir) and graphql-ruby have caught my attention, though I haven’t had an opportunity to tinker with them yet.

That said, I like that GraphQL (and JSON API) let service developers focus on exposing a specific data model and optimizing access to it rather than taking a side quest through REST API design. For application developers, building screens and interactions with the data they need defined inline seems like a big win for contextual understanding.

As ever, the risk of using any kind of mapping layer, whether its objects and relational data or JSON object graphs to downstream service calls, is creating a naive, one to one mapping that create awkwardness and inefficiency.

Refactor the cow paths

Ron Jeffries, Refactoring — Not on the backlog!

Simples! We take the next feature that we are asked to build, and instead of detouring around all the weeds and bushes, we take the time to clear a path through some of them. Maybe we detour around others. We improve the code where we work, and ignore the code where we don't have to work. We get a nice clean path for some of our work. Odds are, we'll visit this place again: that's how software development works.

Check out his drawings, telling the story of a project evolving from a clear lawn to one overwhelmed with brush. Once your project is overwhelmed with code slowing you down, don’t burn it down. Jeffries says we should instead use whatever work is next to do enabling refactorings to make the project work happens. Since locality is such a strong force in software, it’s likely that refactoring will help the next bit of project work. Repeat several times and a new golden path emerges through your software.

In other words, don’t reach for a new master plan when the effort to change your software goes up. Pave the cow paths through whatever work you’re doing!

A few qualities of mature developers

What is technical leadership? Per Mature Developers, it’s a lot of things. My favorites:

So one of the first and most important qualities of mature developers is they’re more often than not paying attention to what is going on around them. They’re deliberately taking their time to observe before proceeding (put succinctly as STOP; Stop, Take a breath, Observe, Proceed).

It is so hard for me to do the stop and breath part.

Sharing the [technical] vision with other involved parties not only serves as a perfect opportunity for practicing one’s skills to explain deeply technical terms and circumstances with non-technical people. It also serves the purpose to validate the vision in terms of relevance to business value and other aspects.

Assessing and understanding risks better puts them into a position where it’s also more likely they’ll actually take risks. Risks which, without the knowledge about business value and the bigger context, may look too big to be worthwhile. But not for mature developers who are able to see beyond the obvious risks and include more aspects into their judgement.

Managing risk, but not overmanaging it: also very difficult.

Previously: Thoughts on “Being a Senior Engineer”.

A few folks suggested I try lazy enumerables to make my extremely chained style practical. I was curious about the actual costs of my style, so it’s time for lies and microbenchmarks! Turns out naively chaining a bunch of maps together isn’t very costly, so go with that to start.

Lazy came in much slower than consolidating the logic in one loop or chaining them without lazy. I thought, I must not have used lazy properly. Turns out, I’m probably showing that laziness isn’t well suited to iterating over collections without an early termination clause (e.g. a take, first, or find) and that for small collections (like an 87-line /etc/passwd), the cost of the lazy plumbing can noticeably outweigh the work done inside the loops. Thanks to Rein Heinrich for talking me to the bottom line!

One idea per line

Lately, I’m doing a weird thing when writing Ruby code. I’m trying to only put one idea or action per line. I’m not sure about it yet.

Here’s what a method to fetch item-y things might look like:

def fetch_items(options={})
  limit = options.fetch(:limit)
  timestamp = options.fetch(:timestamp)
  paged_helper = PagedHelper
  client = OurHttpClient

  responses = paged_helper.
    new(limit, timestamp).
    fetch_pages { |params| client.get(params) }

    map { |r| JSON.parse(r) }.
    map { |h| ItemCollection.new(h) }.
    map { |ic| ic.items }.

For the sake of comparison, here’s how I may have written that method a couple years ago:

def fetch_items(options={})
  helper = PagedHelper.new(limit, timestamp)
  responses = helper.fetch_pages { |params| OurHttpClient.get(params) }
  responses.map { |r| ItemCollection.new(JSON.parse(r)).items }.flatten

I like that the pace of reading the first example is even. You don’t arrive upon some monster line of code that does a multiple things. You don’t have to unpack what’s happening in a situation where you’re calling f(g(h(some_args))). It makes moving lines of code around much simpler because each one is only dependent on what comes before, and not what happens inside. It’s a little easier to write a three-part method, which I really like.

But still, I hesitate. My methods end up about 50% longer. Breaking up the Enumerable transformations into multiple loops instead of one loop doing a bunch of work is probably pretty slow. I have to come up with a lot of names (which is, I think a net good), some of which end up a little redundant.

I’ll let you know how it goes. It may not even survive code review, who knows!

Programming is easier when you know how to stop solving 100 problems with 1 fancy thing and solve 100 problems with 20 plain things.

Ember is probably leading the JavaScript framework pack by supporting releases with security patches for slight more than a year. By comparison, there’s a cottage industry of garages restoring and updating old Porsche sports cars then selling them for ridiculous prices. The USAF (the same one, curiously, that is spending $1.5 trillion on a useless jet, somehow) is going to use their largest strategic bomber, the B-52, for one hundred years.

I’m always thinking about Greg Borenstein’s words when it comes to technology churn:

The constant churn of web technologies hobbles the creation of timeless learning materials and continuity of knowledge across generations.

We should try harder on this.

Code that resists

Kellan Elliott-McCrea, on the way towards an understanding of technical debt, catalogs the ways we end up with code that resists our efforts to change it:

Therefore the second common meaning of “technical debt” is the features of the codebase we encounter in our work that make it resist change. Examples of features that can make a codebase resist change include: poor modularization, poor documentation or poor test coverage. Just as easily though an abundance of modularization (and complexity) or an abundance documentation, and tests encoding the now the incorrect old behavior can apply a strong downward pressure on change.

A little discussed and poorly understood design goal for code is disposability. Given change, what design patterns can we follow that allow us to quickly expunge incorrect behavior from our codebase? Interestingly it is a much more tractable metric for measuring as opposed to more popular criteria like “elegance”. (a post for another day)

Put that in your thinker. Does something like Strategy or Adapter let you throw out whole classes when they prove unnecessary? Or is that so only when you luck out and chose the exact right axes of disposability? Does a microservice really let you discard codebases wholesale? Can maps and functions free you from intertwingled state and behavior or does it move the resistance somewhere else?

Grumpy, opinionated answers: possibly! Even more possibly! Meh. Very meh.