How much do we bend to the will of our tools?

04 Feb 2020

A few months ago, while looking at some code, a little light bulb that I didn’t even know existed went off in my head: “This was only written in this way, because the tools allow it to be written in this way.” Maybe it was a question mark, not a light bulb.

All of us agree, of course, that, yes, with a sufficiently generous definition of tool, the tools we use when programming influence the programs. Programming languages, type systems, testing frameworks, linters, etc. – they’re all tools, in one sense or another and they all leave their mark.

But that’s not what kept me staring. This was different, this code wasn’t just shaped by the language it’s written in, posture-corrected by a linter. No, this code was written by another type of tool.

There are tools that help you write better programs and then there are tools that help you better write programs: auto-formatters, auto-complete, jump-to-definition, documentation lookup, search. The latter is what engraved the code I was looking at.

And I freely admit, even though it might be shocking: I’m not a code savant, I can’t close my eyes, put my hand on a screen and whisper when code was written with which editor (I sincerely wish I could, but don’t tell my parents I said that). Yet I think it’s possible to spot an auto-formatter’s imprint.

Because when you look at the code you simply realize: there’s no other way. We programmers are too lazy. Only with these tools would we write a program in such shape and form.

Here’s a snippet that’s similar in its peculiarities to the one that got me here, take a look:

  const editableTitle =
  			className='editing-form title-editing-form'
  				async evt => {
  					try {
  						const txt = ( as any).text.value;
  						await setTitle(txt);
  					} finally {
  			<textarea name='text' defaultValue={currentTitle}></textarea>
  			<div className='form-actions'>
  				<button className='secondary'
  					onClick={() => setEditMode(false)}>Cancel</button>
  				<input type='submit' value='Update' />
  			{currentTitle} (<a href={url}>#{number}</a>)

A ternary operator spanning 26 lines, in JSX, covering multiple inline functions, one of them using async/await and try/finally. There is a lot going on.

Now let me make it clear: this is not about this particular piece of code. And it’s not about JavaScript, TypeScript, React, TSX or JSX either. As far as I know most developers that work with these tools recommend against this style. You could replace the snippet with a lot of other code written in completely different languages. This particular piece is not even that bad.

It’s merely an example to illustrate my point: I bet you wouldn’t write your code like this if all you had was nano or Notepad.exe. Yes, I bet that long before you would indent a lone ? for 12, 14, 16 or 40 spaces inside another ternary operator, wrapping an inline function, you’d restructure your code.

“Yeah, and if I had to write it with pen and paper, I would’ve quit a long time ago, dude.” Of course. I hear you. And I don’t want to argue that we should go back to punch cards, but this code and all the tools involved in its creation made me wonder: what if the tools we use to write code make us so much better at writing code that we end up unable to work on it without the tools?

If you write text under a microscope, it’s going to end up so tiny that you would only be able to read it while looking through the microscope. What if these tools shape how we write code to such an extent that the code becomes illegible when we approach it without the tools in hand?

They make writing code so much easier by formatting it, moving it around, creating, suggesting and explaining it, but I wonder: do they also help us when we’re not writing new code? Because arguably the majority of our time working on software is not spent writing it: we’re reading code, trying to understand it, slightly tweaking and editing it.

Or did we end up with the programming version of the Omnipotence paradox, writing code that’s so hard to write that we ourselves cannot read it?

Or what if these writing tools only make writing a certain kind of code easier? It’s often said that the actual act of writing the code is the easiest part (“typing is not the bottleneck”) of the whole thing, as if it’s just the manual work, the typing it up, that comes after we made deliberate, concious decisions about a design and its trade-offs. But what if there is a feedback loop between our design choices and what our tools would make easy to type, biasing us against solutions that would require more manual typing?

In concrete terms: would our Java code look different if “Create new class” wasn’t bound to a keyboard shortcut, but instead we’d have a “Show me whether this function is pure or not” key (if such functionality were available)? Can we explain the differences in identifier length preferences between language communities by pointing to the availability of reliable auto-complete in one and lack thereof in another?

Or imagine a far more powerful tool chain than the one we have now, one that would allow us to run multiple analysis passes over our code while we’re still writing it: would we start to write longer functions if we had the ability to hide and show their sub-parts depending on the results of a data-flow analysis, revealing only the parts of the function that relate to the identifier under the cursor in the analysis?

How much of our design and architecture thinking is still bound by what’s easy to type? How much do we bend to the will of our tools? And, maybe most importantly, are we even aware of it?