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.

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.

What if large open source projects appointed a community manager to handle things like codes of conduct and social spaces? Anecdotally, those who make large projects are often the worst at actually running a community. Even volunteer projects need management. Flat organizations will always be dominated by ad-hoc in-group politics. The internet we’ve created thus far is allowing terrible people to outpace good people by a long shot.

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.

Rails doctrine and Kremlinology

Long story short, Rails now has a nicely written Doctrine that delineates the principles that motivate tradeoffs the framework makes. Any Rails developer can benefit from understanding this and coding with the framework as much as possible.

Short story long: I’ve been trying to mentally track this for a while via something I call DHHology. It’s where I follow @dhh and try to piece togther blog posts, tweets, code snippets haphazardly shared, and keynotes to build a mental model for what working with the grain of Rails looks like when you get past a small app.

It’s a lot like Kremlinology, “the study and analysis of the politics and policies of Russia”.

During the Cold War, lack of reliable information about the country forced Western analysts to “read between the lines” and to use the tiniest tidbits, such as the removal of portraits, the rearranging of chairs, positions at the reviewing stand for parades in Red Square, the choice of capital or small initial letters in phrases such as “First Secretary”, the arrangement of articles on the pages of the party newspaper Pravda and other indirect signs to try to understand what was happening in internal Soviet politics.

It’s a bit of a stretch, but I think the same kind of “between the lines” thinking helps to understand Rails. Of course, sometimes I’m wrong, but so were the Kremlinologists. On the other hand, there was that one time I got DHH and Gary Bernhardt to kind of agree on Twitter, so that’s nice!

Software design, always on the wrong foot

Software design has probably been broken from the start. The earliest business software, machine language encoded to punch cards, was more about fiddling registers and managing memory locations than doing arithmetic or implementing business logic. Even after you fast forward to Unix and compiled languages, software is still more about managing heap memory and arcane details like file or error pointers than it is about business logic.

Fast forward again to the first web apps and it seems like there’s an opportunity to put business logic in the center and the incidental complexity of the computer on the outsides. Alas, when web apps took off, most of their logic was written in scripting languages which often trade organizing code along boundaries for the thrill of just getting stuff done. Sometimes I don’t think we’ve outgrown that urge.

Software design has always started off on the wrong foot. Maybe we know better now, maybe we’re as lost as ever. Perhaps in the future I will only feign surprise when I come across working software that is not exactly ideal on the inside.

Specific, purposeful emails are great

When I’m emailing with teammates, I try to do them a few favors.

I make my purpose clear, specific, and up front. I often write the whole email, figure out the real purpose, and then move it into the very first sentence and subject line. I’m a little pessimistic, so I figure I’ve got three sentences, tops, to persuade someone to read an email. They are way more likely to retain at least part of my meaning if there are bullet soundbites for those unlikely to read past the first paragraph. When I want to get down to details, it all goes “under the fold” of the soundbites.

If at all possible, I don’t want to generate Yet Another Meeting. I’ve been in too many meetings that could have been an email. Need to update me on a project? Write it out. Have a simple question to ask? Write it out. Have a complex question to ask? Boil it down to three simple ones, write it out. Need to explore an idea? That’s closer to requiring a meeting! Want to talk about something that requires the sophistication of reading faces and vocal inflections? That requires a meeting, go ahead and schedule one!

What I try to avoid, at all costs, is to throw a bunch of random datapoints or ideas together without drawing a conclusion. Some of the most frustrating emails I’ve read ended with “Thoughts?”. If I’m going to email someone, I’m going to ask a specific question or make a specific point. Ending with “thoughts?” leaves it up to the recipient to guess what the sender wants from the them and then respond in kind.

Don’t ramble, don’t use a meeting when an email will suffice, do make conclusions and do ask specific questions. I will send you email hugs to thank you for respecting my time.

Easy steps to programming language commitment

Feel pressured by other developers telling you that your programming language of choice is old, bad, or that you should feel bad? Apply this heuristic:

  • Try different programming languages until you find one that best fits your brain and the problems you want to solve
  • Use that langauge for everything you can
  • When a language comes along that fits your brain or your problems even better, switch to that one, ad infinitum

Don’t let the hype of people with different brains or different problems get you down.

Code needs boundaries, but not too many

Let’s talk about boundaries in programs. I need them, otherwise programs grow increasingly inscrutable and impossible to change. A lack of boundaries is nearly as bad as spaghetti code; i.e. it’s really bad.

But, too many boundaries can also make a program inscrutable. To the absurd, a program composed entirely of black boxes each of a single narrow function and behavior is all indirection. Indirection is a cost I pay when I introduce boundaries, e.g. “Your princess is in another castle.” I want to have just the right number of boundaries; not too few, not too many.

Further, I want to avoid establishing the wrong boundaries if I can. Boundaries are hard to move around; creating them is an implicit act of making some kinds of changes more difficult. Awkward boundaries make it difficult to write correct code; hurried developers will yield to the temptation to circumvent the boundary. If you do manage to identify an awkward boundary and correct it, you’ll have some temporary churn in your program while you rejigger the boundary and the code on both sides of it.

On the other hand, the right boundaries are wonderful. They create leverage for the developers working on both sides of the boundaries. They get more done, only needing to know about the boundary and not what lies on the other side. Establishing good boundaries is the first step towards encapsulation and abstraction.

We used to make boundaries from packages and libraries. Now we have added out-of-process services, message passing, and infrastructure as boundaries. This will probably turn out as a net benefit, but right now we’re chasing novelty, blog posts, and conference talks at the expense of increased complexity. We aren’t really “engineering” our boundaries.

Creating boundaries too eagerly increases the odds of imposing the wrong boundaries and churning on said boundaries. Creating boundaries too lazily imposes a high cost of change to create those boundaries once you discover them and accept the implementation challenge.

My favorite kind of boundary is a bounded context. Its a wonderful epiphany that we don’t all have to agree on the precise definition of words and responsibilities if we can agree where the fences (boundaries) go.

Gary Bernhardt has nice things to say about boundaries. If you like this, you’ll love his ideas.