writings
The simple problem inside the complex one
A sophisticated solution to a complex problem is fun to find. Its even fun when someone else finds the solution and thoroughly writes it down.
Despite the thrill of recognition for tackling a big complex problem, I find it's more practical to seek the simple problem lurking in and around the big problem. Not all tricky problems are complex. Some are presented in a complex way, some are complex because of restrictions that are easily worked around, and some are complex because of an adjoining social problem. Find the simple problem, or the social problem, and solve that instead. It often works for me.
My favorite kind of solution is one where the solver has taken a problem with a big surface area, found the core of the problem that is 80% of what people care about having solved, and then solved that tiny subproblem. If you want to see masters of this approach, check out the works of Blake Mizerany and Ryan Smith.
Your application is on fire
Six easy pieces on thinking about sustainable code
Your application is on fire. Something is consuming it, a process converting fresh and shiny code into rigid, opaque legacy code.
Seriously, your applications is on fire. You should look into it.
Are you going to fight the fire? Will you keep throwing logs into it, hoping that you can corner it at some point down the line? Do you draw a line in the sand and start containing the fire and eventually suffocating it?
The fire is every change you make to your application in haste. The fire is every design decision that is bolted onto one part of the application without care for how it fits into another part of the application. The fire is every stopgap put in place to fix a short-term problem that isn't revisited once the short-term has elapsed.
Once time is not the most critical concern, you stop and think about how to make the application better in the long term. You consider what worked about the stop-gap solution, how to design your application with care, how to iterate on improving the architecture of your application.
You fight the fire by trading time for quality. Knowing how to solve your problems, knowing what is important about your application and what is consequential, you start to express a design. You write down principles that worked for you and write down an architecture that joins the principles and the design in the middle. That's where your application lives, once it's not on fire.
The reason green field software projects are appealing is that they aren't on fire. A green field is lush with possibility, without the constraint of decisions already made. More so, a green field is not bound by decisions implicitly accepted but never considered.
The unconsidered decision is how the fire starts. A class here, a subsystem there. Soon you've got a system but no design, emergent or otherwise. You've got a ball of mud, but the mud is on fire.
That fire is sustained by every time you bolt something else on. It's sustained by implementing the feature or fixing the bug without care for the program as a whole. It's fed by making progress without making care.
The fire is warm, but eventually it will consume your application. No one wants to work on the burnt out wrecks inside of it.
Everyone can smell it, but no one wants to go near it. Firewalls are erected, teams establish practices that allow them to avoid working on or near the fire.
So it burns on.
One day, you decide enough is enough. No more fuel on the fire. Every change to the application has to meet standards: does this make the code better, does this make the design better? This here is the design; adhere to the why's, use your creativity for the what's and how's.
That day is the day you start thinking for the long term. In the language of economics, its when you internalize external costs. You accept that the cost of maintaining this application is worthwhile, so you should mitigate that cost, optimizing input effort for output value.
You can't maximize cost by just getting the work done. You can't maximize value by building grand structures. You optimize cost for value by continuously making decisions that mitigate or reduce the fire.
Internal combustion engines are made of fire. Despite being powered by flame, combustion engines are pretty mundane things. They became mundane because they have a design and a standard of engineering that contains that fire, harnesses it, and turns it into a mechanism for converting fuel into energy.
When do we harness and focus our code, instead of letting it burn inefficiently and out of control? The moment we do is when we stop being firefighters and start being the wizards we set out to be when we started writing software.
What makes longevity?
A joke for a late-night variety show monologue may only be funny for one day (e.g. a joke about a celebrity). A newspaper article may lose relevance in days or weeks. A TV show might feel dated a couple years after its run ends (e.g. most problems on Seinfeld could be solved with a smartphone). Computer programs don't fare well over time either (though there are exceptions).
The best songs demonstrate better longevity. Beethoven and Bob Dylan still work today. There will always be something amazing about "Good Vibrations", at least to the trained ear.
Even the rap trope of yelling the year the song was recorded has a timeless quality to it; it serves as a marker for the state of affairs. "Nineteen eighty nine!" is the first thing shouted in Public Enemy's "Fight the Power", marking the historical context for what Chuck D is about to tell us.
Why is this? I suspect it underlies the act of making music. Besides hit factory music, i.e. ear worms you hear on the radio, a musician's goal is to make something expressive. Expressiveness often leads to qualities that give a piece endurance; timelessness, nostalgia, high quality. Expressiveness is less often an objective for jokes, headline journalism, or television.
That enduring quality, it's tricky. It happens in film, television, and books too. But, for me, there's something about music that has a more direct emotional connection. They vibrate my ear drum and work their way directly to a part of my brain containing "the feels". I hear a good song and I'm immediately thinking about why I enjoy it so much, what makes it so good, or when I heard that song and connected it to an experience.
Maybe that's why music is such a big deal in our culture. Really, really good music connects in a way beyond "haha that's funny" jokes or "huh, that's interesting" writing.
Is it possible to write expressive non-fiction or an enduring computer program? It seems like the answer is yes, but the answers are outliers. Hofstader's Godel, Escher, Bach and Knuth's TeX come to mind. For every GEB or TeX, there are thousands of less interesting works.
Further, the qualities of an enduring, expressive, and yet functional work seem somewhat at odds with the pragmatics of the daily act of writing or programming. A lot more perfectionism, experimentation, and principle goes into these works than the typical news article or application.
And yet, for every "Good Vibrations", there's probably a thousand commercial jingles composed, elevator tunes licensed, ringtones purchased, and bar bands playing "Brickhouse" yet again. Perhaps music is just as prone to longevity as writing, film, or programming but has a far longer timeline on which its easier to see what really worked. In fifty years perhaps, if we're lucky, we'll start to learn what is really amazing in film and software.
Lessons from premature design
Lessons from Premature Abstractions Illustrated. I’ve run afoul of all three of these:
Make sure you have someone on the team or externally available that will keep the critical, outside look at the project, ready to scream and shout if things turn bad.Don’t let your technical solution influence your design decisions. It’s the tool that needs to fit the job, not the other way round.
Don’t build abstractions as long as you have no proven idea on how the levels below that abstraction will look like.
I could have used an outside, trusted voice to gently reel me in if when I went off into the unproductive weeds. Someone to ask “how will this help the team in two weeks?”, someone to point out ideas that might be great but have only achieved greatness in my head. A person who is asking questions because they want me to succeed, not because they’re trying to take me down a notch.
I have rushed into implementing the first idea in my head. Sometimes I’ve convinced myself that my first idea is the best, despite knowing I need to review it from more angles. I’ve jumped into projects with a shiny new tool and a bunch of optimism, only to cut myself on a sharp edge later on.
I’ve built systems that look fine on their own, but don’t fit into the puzzle around them. I’ve isolated myself building up that system, afraid to figure out how to fit my system into the puzzle in a useful way. I’ve used mocks and stubs to unintentionally isolate myself from the real system.
Basically, these are all really good ways to paint yourself into a corner. It seems like being in a corner with a shiny new system/tool/abstraction would be nice. Unfortunately, my experience is that once you have to make sense of that abstraction in a team, things get dicey.
It’s dangerous to run a software project on your own! Take a friend.
Reflecting on Ruby releases
Ruby 1.8 brought us a couple changes that made many kinds of metaprogramming easier, plus a whole bunch of library additions that made Ruby feel more "grown up". Without seeking external libraries, one could write Ruby to solve many problems developers face in commonplace jobs. I wasn't around for Ruby 1.6, but I've been thinking of Ruby 1.8 as a transition from "better Perl or Java" to "better Smalltalk".
Ruby 1.9 brought us features that make some functional programming idioms easier. Lambdas, i.e. anonymous functions, require less syntax and are better defined. Enumerators make it possible to use features of Enumerable, itself a very functional-esque feature, in more places. Symbol-to-proc makes it easier to pass methods around as blocks, another FP-esque practice. I might say that Ruby 1.9 is the "better MatzLisp" version of Ruby.
Ruby 2.0 is bringing us features that, on the surface, make it easier for Rails to extend the Ruby language via ActiveSupport. I think that's too shallow of a reading. The new tools in Ruby 2.0 (excepting the highly-controversial refinements) make it easier to cleanly add functionality to Ruby's core objects and library. Reducing the cost of extending the core make it possible for more libraries and applications to judiciously make high-leverage additions to the lower levels of Ruby. That seems like a pretty good thing.
I can't find a source for this, but I could have sworn I once read that all programming is language design. It was probably related to Lisp, where you're arguably directly manipulating the AST much of the time. If the changes in Ruby 2.0 can take us closer to this level of program design, where we think more about building language up to the problem domain instead of objects and mechanism, sign me up.
Design for test vs. design for API
How many design considerations are there in an almost trivial method? Let's look at two of them. Consider this code:
def publish!
self.update_attributes(created_at: Time.now)
end
If you've been studying OO design and the SOLID principles, using TDD as a practice to guide you towards those ideas, there's a missing piece here. The reference to Time
is a dependency that should be injected. In Ruby, it's really easy for us to fix that:
def publish!(time=Time.now)
self.update_attributes(created_at: time)
end
I suspect a lot of TDDers would instinctively write the above first, skipping the first version by force of habit. But, let's stop and think about what the drives us to want the second version.
The strength of the second version is that it is designed for test. If we need to test how this model behaves when it is published at night, or on a leap day, or the day before Arbor Day, injecting the time object makes that easier.
There are some other test-focused design direction this method could go. We could create our own object whose role is to hand out timestamps, which would allow us to reasonably stub out the time reference, instead of injecting it. I'll bet there are other approaches lurking out there as well.
I want to look at another set of design considerations. I could design this code for testability, which often leads me to code that follows the SOLID principles which often leads me to decoupled code that is easier to change later. To many people, that's a good thing.
However, there's another lens I can look through: API design. How does this method hold up as a piece of behavior that developers will leverage?
Strictly speaking, the TDD'd version is a more complicated API. Even adding one optional parameter to a method carries "mass". Consider documenting the parameter-less version:
Publishes the current post. The
created_at
timestamp is set to the current time. Returns thecreated_at
timestamp.
For numbers sake, it's 40 words. More importantly, it reads linearly. Now let's look at the dependency-injected version:
Publishes the current post. By default,
created_at
is set to the current time. Optionally, callers may pass in aTime
object, or any object that returns aTime
object when sent thenow
message. Thecreated_at
column is set according to thatTime
value. Returns the value of thecreated_at
timestamp.
This one is 54 words. That's not too many more, numerically, but notice that the explanation is no longer linear. There's a default, easy case where I don't care about the timestamp. Then there's a clever case where I do care about the timestamp. In real API documentation, I'd need to specify when and why I'd want to use that clever case and what it looks like.
There's some further potential trouble lurking in this API. What if a caller passes in the wrong kind of Time
object? What if sending the now
message raises an exception? Those are important parts of the API too, both from a behavior specification perspective and when considering the user experience of using this API in code and possibly troubleshooting it when things go wrong.
My point is, that optional argument is starting to look rather weighty. Adding the code is pretty trivial. The possible interactions with the optional argument and its support cost is where it gets expensive. Like many things, it's a trade-off.
I won't claim to know which of these is better. Honestly, I think it comes down to a subjective view on what's important: test design, or API design. This is where I can't make a bold-sounding prognosis. I believe that design, even of code, is about deciding what to leave out. Everyone has to decide what to leave out for themselves.
Declaring coupling
A lot of discussions on software design end up focusing on dependencies and coupling. In short, hell is dependencies and the couplings it produces. It's a tricky problem because its hard to look at some program text and see all of its dependencies; some of them require intelligence to recognize.
In Ruby, we don't have very good ways to declare a class's dependencies and no ways ways to declare its couplings. We can describe a project's dependencies with a Gemfile or a file's dependencies with requires. The trick is that these specifications often explode in complexity. Requiring Ruby's thread library brings in some thread-safe data structures like queues and condition variables. Requiring ActiveRecord brings in a world of dependencies and causes a number of behavioral changes to Ruby that some consider impolite.
In some tinkerings with Clojure this weekend, I was struck how the ns
function is more effective at both declaring dependency and coupling and in restricting the possible distress those qualities may bring. Consider this snippet from my weekend project:
(ns hrq.routes
(:use compojure.core
hrq.core)
(:require [compojure.route :as route]
[compojure.handler :as handler]
[ring.middleware.params :as params]))
I have pedantic quibbles with ns
, but I like what's happening here. This file can only use the functions in compojure.core
and hrq.core
with no namespace qualifications. This file can only use the functions from compojure.route
, compojure.handler
and ring.middleware.params
when they are qualified with the proper prefix. So now I have a very good idea of what code this particular file depends on and where I should look to find behavior that this file is subject to.
To a lesser extent, I have a good guess about what state this file depends on. If there are dynamically scoped variables (pardon me if those are the wrong Clojure/Lisp words) in the dependencies declared for this file, I would need to care about them. If those files are pure behavior (i.e. referentially transparent pure functions), I have nothing to worry about.
Clojure isn't perfect in this regard; it does allow mutations and state changes outside of functions. It's not strictly referentially transparent like Haskell is. The tradeoff is worthwhile, in my opinion. Admit some possible coupling in exchange for ease of building typical programs.
I'm not sure that Clojure is inherently superior to Ruby in this regard. It's possibly a momentary cultural advantage, a reaction by those who were burned by expansive, implicit dependencies in Ruby and other languages. That said, it's a good example of Clojure’s considered separation of concerns solving problems that are quite thorny in other languages.
Intermediate variables, organizing OO, meeting Grinders half way
I work with Dave Copeland at LivingSocial, but not on the same team. Maybe someday I’ll fix that, but for now I learn a lot from his writings. Herein, a few things worth checking out yourself.
If you ever need to read my code, you’ll eventually come to suspect I have a particular dislike for intermediate variables. You’ll come to suspect this through finding lots of uses of inject
and tap
, two Ruby methods not everyone is on good terms with. You can imagine I’d side with Dave on the subject of Tap versus intermediate variables. You’d be right, but Dave says it so well, you should read his take on the joy of tap
. He also shows how to annoy people with tap
-like constructs in other languages. If you’re into combinators, Reg Braithwaite has written about tap
in terms of Kestrels.
I’ve been learning a lot about how to think about organizing a non-trivial object-oriented system this year. Gary Bernhardt is doing some fantastic work explaining a hybrid imperative/object/functional system. If you don’t have time to dive into Gary’s entire backlog (it’s worth it to find the time), Dave covers some similar ground describing the only four types of classes in your OO system. Think of these as a post-hoc observation on how many systems seem to evolve; Record objects take root, Service objects reveal themselves (often intertwined amongst other objects), Builders are sprinkled throughout, and there are a few classes hanging out that you wish you’d made immutable. These are handy guides for thinking about and refactoring an existing design. That said, I think it would be overkill to start a design with these archetypes. Caveat: some developers will really dislike organizing a system this way; tread carefully.
I’ve written about the virtues of The Grinder. I know a lot of non-Grinders wish that the Grinder knew more about how to take the code they’ve made to work and improve it so that it is more malleable in the future. Making it Right: Technical Debt vs. Slop sets out a good mindset on how this can happen. Think before you type, write a test, make it work, and then tidy it up with future malleability in mind. From there, non-Grinders need to meet Grinders in the middle is in shrinking the feedback cycle. When tests are too much effort to write or take too long to run, Grinders fall back to their old habits. When making it right involves too many intermediate steps with nothing to show for it, Grinders move on to the next thing. When a non-Grinder learn to be less precious with our work, or a Grinder learns to take a moment to round off the sharp corners on their work, you end up with a much stronger team. Fight for it.
Why I'm down on hypermedia containers
In response to my hypermedia opinions, Mike Kelly said:
These two seem to conflict: “In my opinion, abstract container formats aren’t useful.” and “Just use JSON”. People normally talk about “generic” media types, but they don’t have to a “container” at all, they can simply add conventions for linking. Having conventions for this stuff is useful because it allows us to build tooling around it, if everyone reinvents the wheel in their own way then we can’t build re-usable code. For a similar reason, “specifying your own custom MIME types” is not a good idea – there’s also the time cost associated with doing that. If you use something like hal+json you avoid that cost, and can concentrate on establishing your API’s workflows via link relations.
The logic behind the madness goes like this: abstract containers aren’t solving a problem I currently have. Unfortunately, this means they create problems for me. In the end, I’m building an API to provide functionality, not as advocacy.
As of summer 2012, there are ideas like HAL, JSON collections, etc. and specifications of those ideas. There are very few implementations. As a service provider, I wouldn’t actually get any benefit out of using those formats. The convention, within my own API, that fields ending in _url
are links is sufficient. I’d actually end up net negative, because I’d have to explain how e.g. HAL works and support client developers seeking to understand how to work with it. Anyone building to my API would likely end up having to write their own HAL code, so they don’t benefit much either.
I’ve decided to use JSON because providing an API that returns HTML and expects users to scrape it via selectors is extremely confusing to developers. Keep in mind, not everyone is savvy to the latest development trends. To them, an API means an HTTP service that returns XML or JSON. If I were to embrace HTML as a response type, I’m again stuck with explaining a new concept to client developers.
If it’s not obvious yet, one of my main principles in adopting hypermedia is to avoid educating developers on hypermedia as much as possible. I’m in the game of providing a useful API, not a system that shows off the possibilities of hypermedia and how deeply committed I am to its theories.
Finally, I’ve chosen to craft my own content types because I need some kind of contract with client developers that tells them what kind of data they can expect to see, plus some documentation that expresses that contract in a way that is easy for humans to understand. An RFC-style specification that states what a content type MUST/MAY/SHOULD include is exactly the right kind of abstraction. It allows me to update the specification with a version identifier and specify how each revision changes in terms of what data is available. Further, I found a content type the most tractable solution for specifying the input formats supported by PUT and POST endpoints. None of the abstract containers that existed as of August 2012 fit my needs for specifying links, structure, and how to submit data via POST
/PUT
/PATCH
.
Honestly, link traversal and machine-to-machine interaction are not the pain I’m feeling. What I want is the simplest possible API that allows potential client developers to understand what they can do via our API and how to do it. Further, I want it to be possible, even if it’s not magical or easy, to change the API in the future so I’m not constricted by its current design. I feel no need to apply all of the hypermedia principles to make something useful; I can cherrypick some of the hypermedia principles and still achieves the goal of an API that is stable but not set in stone.
Hypermedia opinions
Through the Gowalla API, and now the Sifter API, I’ve worked with a couple systems one could reasonably call a hypermedia API. Since smart people are talking about them today, I feel compelled to throw in my two cents.
A URL-based API alone won’t prevent breakage, as many point out. It won’t even prevent people from figuring out how to extract IDs and continue to bang URL strings together. I don’t know why creating URL strings is the security blanket of many API client developers, but it seems you can’t take it away from a few of them.
That said, a URL-based API does make life easier for good actors. Client developers, especially the ones that can’t upgrade their deployed applications quickly, can sleep better knowing that the upstream API could change and their code won’t break. Service developers can rest easy knowing it’s not easy, but it is possible to change their URL structure if they need to.
A hypermedia API means you can’t skip on documentation. In fact, it means you probably need better documentation. You’ll need to explain what each link in the API responses means (is photos_url
the URL for my photos, or my friends’ photos?) and what kind of data they can expect that URL to return (a photo object? a collection of photo objects? a user?) As I mentioned before, a lot of client developers don’t have their minds wired for hypermedia yet, so you’ll need a lot of examples for how they should build their clients.
In my opinion, abstract container formats aren’t useful. Squeezing your application’s data into someone else’s data model is not a great place to live. That said, I do think specifying your own custom MIME types is a promising idea. You specify each of the content types in your API, RFC-style, and then provide examples of that data. Should you need to change your response formats, you update your RFC-style specification, adding a new field as a “MAY provide” type feature.
The rub of this approach is that you can end up with an explosion of response-type handling code in your application. Again, it’s not that hypermedia principles make it easy, they just make it possible.
I’ve seen some hypermedia APIs expressed as hypertext, using HTML and cleverness. I don’t see the benefit of this. Just use JSON, and maybe specify your own response types.
The shortcoming in much of the hypermedia content out there is they focus on clients reading data and hand-wave over clients that need to create or update data. I can see why; I think that machine discoverable parameters are a tarpit. Trying to tell a client what fields the server will accept, what types are accepted, and then handle all the possible error flows is verbose and riddled with edge-cases. The way I’m solving this is to specify, in the documentation, which URLs clients can expect to POST
/PUT
/etc. data to and what content-type those URLs expect to receive. I’m pretty much doing the least hypermedia-ish thing possible, but I think that’s the correct choice right now.
The great thing about the principles of hypermedia APIs and its discourse if a focus on workflows. The biggest mistake I’ve made in building APIs is in exposing a database instead of a service. No one cares what your table structure or domain model looks like. They want to fetch some data, preferably indexed in a way that is immediately useful to them, and maybe write some data back. When you look through this lens, API design starts to look a lot like UI design. It’s fun!
You should read about hypermedia APIs, if only to challenge your thinking. It helped me a lot to do just that. Check out Steve Klabnik’s Designing Hypermedia APIs; it’s not perfect, but it makes a lot of these things easier to grasp.
In the end, I think designing, building, and supporting a hypermedia API is hard. You may be better off with an RPC-over-HTTP, or ID-based REST design. The good news is you can benefit from an incremental application of the principles of hypermedia APIs; you don’t have to go all in before you get something out of it.
Needs better words
How much easier would Haskell be if its vocabulary wasn’t so deeply rooted in abstract mathematics? How many more people would immediately understand Cassandra if it just adopted the vernacular of row-oriented databases instead of overriding it with column-oriented semantics? Wouldn’t the programming world be better if every language didn’t call itself object-oriented?
So many programmer-facing things could be better if they had better names for concepts. The power of good naming:
Characters are cheap, confusion is costly. Let’s not make things harder for the programmers who come after us. Remember, this is just as likely to be ourselves in a few months. Let’s avoid using a name like prj when project is only four characters more typing. Anything that reduces reading friction in our code is a good thing.
The cynical view is, we are actively confusing and inhibiting ourselves when we use names that are too short, not clear, or outright wrong. The optimistic view is that there’s a progression we can take from not knowing what something should be named, to giving it an acceptable name, to using the naming process to discover better structures.
If you ever hear me say something “needs better words”, it means that I think the idea is right but the labels are wrong. A philosophical dialog may ensue, where I struggle to discover what the essence of the idea is. The end goal is a word that is concise, has the right connotation, and whose meaning is obvious and accessible to as many audiences as possible.
I love naming things.