2016
Our current political Trolley Problem
As self-driving cars inch closer to a daily reality, the Trolley Problem seems to have entered our lexicon. In short, should a self-driving computer choose to avoid hitting a bunch of people and kill its single occupant as a result? Turns out people expect the car to protect the greater good second and their own skin first.
Maybe out our current political environment of unfettered gun violence, climate change, Trump-lead racism, Brexit-fueled xenophobia, and general apprehension about losing what we thought we’d earned are a kind of longer-term but still serious Trolley Problem. Would you vote to improve society at large even if it meant taking yourself down a ego/prestige/money notch?
Well when I put it that way, things seem pretty bleak!
I happened across an Alan Kay essay, Enlightened Imagination for Citizens, and it kinda helped me get through that bleakness. Some highlights:
In a raging flood, a man risks his life to save a swept away child, but two years earlier he voted against strengthening the levee whose breaching caused the flood. During an epidemic people work tirelessly to help the stricken, but ignored elementary sanitation processes that could have prevented the outbreak. More astoundingly, as many as 200,000 Americans die each year from diseases spread by their own doctors who have been ignoring elementary sanitation (including simply washing their hands when needed), but who then work diligently to try to save the patients they have infected. Studies show that about 80% of Americans are “highly concerned” about climate change, yet this percentage drops to less than 20% when the issue is combined with what it will cost to actually deal with these changes.
Regarding our inability to reason about dynamic systems:
One of the reasons the consequences were not imagined is that our human commonsense tends to think of “stability” as something static, whereas in systems it is a dynamic process that can be fragile to modest changes. One way to imagine “stability” is to take a bottle and turn it upside down. If it is gently poked, it will return to its “stable position”. But a slightly more forceful poke will topple it. It is still a system, but has moved into a new dynamic stability, one which will take much more work to restore than required to topple it.
On acting now instead of acquiring a perfect answer or solution:
When the costs of an imperfectly understood event are high or essentially irreversible, measures have to be taken even when perfect proofs are lacking. This idea is understood by most developed societies—and carried out in the form of levees and pumps, food and water stocks, etc.—but is nonetheless resisted by many of the voting public.
Perhaps the solution is to get ourselves representatives that excel at reasoning and legislation instead of politics and fundraising?
One of the reasons we are a republic with a democratic base is that the representatives can be selected to be “the best and the brightest” from the population as a whole (this was another early ideal for the great American experiment). We could argue that the current representatives are “all too representative”, but this is part of a slide in our political and social systems that needs to be shored up and improved. The idea of “national service” is now just a whisper, but it is what needs to be brought back into the forefront of what it means to be a citizen.
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”.
I love when snares don't keep time
In the majority of music you’ll hear after 1960, the drummer does most of the time keeping with their snare. On 100% of Bruce Springsteen songs, time is kept entirely with the snare. I listen to a lot of The Boss; it’s a little surprising when I don’t here a consistent 1/3 or 2/4 snare keeping time.
That makes the drumming on most jazz albums pretty delightful. For example, Cannonball Adderley, “Games” (Roy McCurdy on drums):
We should make jokes about tech millionaires
I try not to respond to the bullshit in this world with “this person is awful and they should feel awful”. Except for politicians. I try not to participate in witch hunts. I cope via jokes and satire.
After making a few jokes about Paul Graham at RubyConf, a fellow asked me why I made fun of that poor kingmaker (not his words). In short, I think everyone should make jokes about multimillionaires, especially Paul Graham. He’s a celebrity-of-sorts, making the idea of Paul Graham completely open to satire and ridicule. My favorite such satire was a composite character from Silicon Valley who, due to the actor’s passing, will sadly not recur on the show. So it’s up to us, the unwashed internet people, to poke sticks in his platonic sides.
The thing to illuminate is how past Paul Graham used to have the analytical and rational skills to tell when someone like current Paul Graham is acting a fool. Graham suffers from confirmation bias and billionaire bias. He thinks his rational skills are still sharp enough to help him write about extremely tricky and irrational topics like diversity or inequality and he thinks his monetary success makes him doubly qualified to write about these topics from his own first principles. In other words, Past Paul Graham should know enough to tell Current Paul Graham when he’s out of his league.
I feel Paul Graham is an example of the geeks-shall-inherit-the-world and corruption of money that is bullshit in this world and everyone should apply satire to him whenever possible.
Why I blog in bursts
I write here in bursts. It confounds me as to what marks the beginning and end of those spikes. I have a few hunches:
- ambitions grow larger than my free time: it’s easier to hit publish on a self-contained thought than a connected series or magnum-opus essay
- intervention of life: work, vacation, various chores adults are expected to perform
- self-distraction: acting as a novelty junky rather than pushing one thing through to completion
- tweeting less: putting little thoughts into tweets means I’m driven to put slighly-not-little thoughts into blog posts
- reading less: reading interesting things drives me to (attempt to) write interesting things
- skipping record: I worry I’ve already had this thought and published it somewhere
Also sometimes I’m not quite sure how to end a thought like this and I wonder if I should worry about that and then I decide to let it slide.
Extra Ruby chaining, not that costly
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) }
responses.
map { |r| JSON.parse(r) }.
map { |h| ItemCollection.new(h) }.
map { |ic| ic.items }.
flatten
end
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
end
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!
Less fancy
Programming is easier when you know how to stop solving 100 problems with 1 fancy thing and solve 100 problems with 20 plain things.
How does a bomber outlast a JS library?
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.
Tinkering with Kinto
Here’s a thing I want to experiment with. Short videos talking about what I’m currently tinkering with. Here’s one!
[wpvideo lj3gGXFS]
More notes in the repo, if you want to play along at home. Let me know what you think!
Three part method
I find methods/functions decomposed into three parts really satisfying. Consider a typical xUnit test:
def test_grants_new_role
# setup
user = make_user
new_role = make_new_role
# behavior under test
user.add_role(new_role)
# assert results
assert_equal [new_role], user.roles
end
Lately I’ve been structuring Rails controller similarly:
def create
# Extract inputs/parameters from HTTP request
person_params = params.require(:person).permit(:name, :age)
# Invoke behavior encapsulated in a Plain(ish) Ruby object somewhere
user = UserService.create_user(person_params)
# Check the result and make some HTTP output
if user.persisted?
redirect_to user_path(user.id)
else
@user = user
render :new
end
end
Clojure even has the let
form which encourages this style:
; annotated from clj-http
; https://github.com/dakrone/clj-http/blob/master/src/clj_http/util.clj
(defn gzip
"Returns a gzip'd version of the given byte array."
[b]
(when b
; set the table
(let [baos (ByteArrayOutputStream.)
gos (GZIPOutputStream. baos)]
; do the work and clean up
(IOUtils/copy (ByteArrayInputStream. b) gos)
(.close gos)
; produce a result
(.toByteArray baos))))
I don’t think there’s anything inherently wrong if a method or function isn’t organized this way. But when I read code structured this way, it feels less like a bunch of random logic and more like a cohesive unit that someone put time into thinking through how someone might try to understand it later. The Rule of Three rules everything around us.
My favorite beef is O'Reilly vs. Graham
Of all the pop culture beefs going on at the time of this writing (Meek vs. Drake, BoB vs. Neil deGrasse Tyson, Trump vs. Everyone), my favorite is now Tim O’Reilly vs. Paul Graham on income inequality.
When a startup doesn’t have an underlying business model that will eventually produce real revenues and profits, and the only way for its founders to get rich is to sell to another company or to investors, you have to ask yourself whether that startup is really just a financial instrument, not that dissimilar to the CDOs of the 2008 financial crisis — a way of extracting value from the economy without actually creating it.
This has always bugged me in particular. So few startups have an idea beyond "get smart people together, maybe make something, hope that selling the team ends up profitable". We need a much better word for "speculative technology-focused company funded by speculation".
Things I’ve noticed San Franciscans deeply despise:
- housing prices
- nearby events that aren't actually held in San Francisco (e.g. the Super Bowl)
The future of programming is design, teaching, and empathy
The Future Programming Manifesto starts with this header:
Inessential complexity is the root of all evil
OK, I’m on board!
We should measure complexity as the cumulative cognitive effort to learn a technology from novice all the way to expert. One simple surrogate measure is the size of the documentation.
Perhaps we could describe the complexity of a technology in “bookshelves”? For example, in my second internship I met a CleearCase administrator whose office bookcase had one shelf devoted to SunOS, one shelf to Oracle, and the final shelf dedicated to ClearCase itself. How many bookcases for Ruby, Rails, JS, CSS, a database, and all the other stuff you need to know to put a CRUD app in your browser (not even deploy it to the web!)
- Maintaining compatibility increases complexity.
- Technical debt increases complexity.
- Most R&D is incremental: it adds features and tools and layers. Simplification requires that we throw things away.
- Computer Science rejects simplification as a result because it is subjective.
- The Curse of Knowledge: experts are blind to the complexity they have laboriously mastered.
- Rewarding programmers for their ability to handle complexity selects for those who love it.
- Our gold-rush economy encourages greed and haste.
A weird thing about programmer is that those that rant endlessly about someone else’s complexity, layers, and haste are almost completely blind to the complexity, layers, and haste they make in an effort to set the world just so.
We should work for end-users disenfranchised by lack of programming expertise. We should concentrate on their modest but ubiquitous needs rather than the high-end specialized problems addressed by most R&D. We should take inspiration from end-user tools like spreadsheets and HyperCard. We should avoid the trap of designing for ourselves.
What if more of programming was accessible as data manipulation (cf. spreadsheets, data files, JSX templates) instead of as logic and behavior (i.e. almost every programming language)?
We are doing Design: using experience and judgement to make complex tradeoffs in order to satisfy qualitative human needs.
This reminds me of Developer Experience. “Developer experience” is a weird word right now, but it’s becoming table stakes for success. It’s a design discipline. It’s considering the form and function of code. It’s the opposite of attempting to learn C ;)
Long story short: we’re gonna need more empathy, more design skills, and more teaching skills to reach the next level of great programming languages and tools.
One model doesn't fit all
There are two kinds of developers in the world:
- those who realize data models aren’t monolithic and use business boundaries to their advantage
- those struggling with monolithic data models
Versioning an API is a river delta of pain
Slight rant: versioning a (REST) API inflicts upon you a confluence of factors that will lead to pain no matter what you do.
You’re going to need to version things, which opens you to bikeshedding which True Scotsman approach to REST versioning you’ll use. Once you’ve expended tons of effort on how clients should specify which version they want (i.e. once you’ve just barely started), now you need to figure out how to make that work in your code. Which, after you’re done parsing the HTTP request (the easy part!), is almost certainly going to lead to some unruly layer(s) of indirection. At which point you’re going to hate life and never want to introduce another version ever again. And you won’t even be close to finished.
I hope that, between JSON API and GraphQL, letting the client specify what they want ends up proving way better than relying on the server to carefully (or possibly carelessly) hand craft just the right data for the client.