Software as Craft and the Passion Project

I firmly believe that creating software is a craft discipline. Like other craft disciplines it draws on scientific and engineering knowledge, but reaches its greatest heights when it marries that with details about human ergonomics or aesthetic flourishes intended purely to delight.

Software as Craft and the Passion Project

I firmly believe that creating software is a craft discipline. Like other craft disciplines—painting, carpentry, masonry, garmentmaking in all its forms, etc—it draws on scientific and engineering knowledge, but reaches its greatest heights when it marries the empirical with the intuitive and subjective, with details about human ergonomics, or aesthetic flourishes intended purely to delight.

Also like other craft disciplines, it can suffer when productized and pushed to scale: delightful details can be sacrificed on the altar of efficiency, revenue, or profits; individual style can be whittled down for "standardization," making it easier for teams—or assembly lines of workers—to produce large volumes of consistent product. Portions of or even the entire production can be mechanized and automated, which can lead to output that it somehow colder, more distant. Less human.

It doesn't have to, though. Technology and process can accelerate creation, can reduce or eliminate drudgery and rote repetition, and free human creativity to emphasize those unique elements, wonderful digressions, and happy accidents that spark joy in the things we make. This is as much the thesis of Lightbox as it is my approach to the creation of Lightbox itself.

Freed from financial imperative as the raison d'être for Lightbox, I have made software-as-craft a part of my goal: to write this application in the most elegant, most expressive, most performant ways I know how. This establishes a number of competing constraints, and requires some hard-won experiential compromises to resolve:

  • pursuing the absolute best way to build a piece of software can lead to constant rewrites of internal components with nothing public to show;
  • discovering the absolute best way to build a component can lead to long, digressive journeys learning entirely new things—oh, hey, I finally focused on mastering fragment shaders—with no forward progress on the overall project;
  • the environment in which the software is being written, and the platforms for which it is published, are themselves moving targets, meaning that "best" can be upended at any time: since first conceiving of Lightbox, I have migrated to a new programming language (Swift) and, recently, a new primary interface toolkit (SwiftUI; more on that in a bit).

The compromise is to look at the release process itself as iterative, and an important means of collecting feedback. Steve Jobs (apocryphally) said, "Real artists ship." [For non-software folks, we speak of "shipping" software to mean releasing it to the next consumer in its value chain, whether that is another developer on your team, another software/hardware development firm, or an end user.]

I have therefore made a set of choices—chosen a set of tools to work with, here and now—and established targets for release to the public, to validate my hypotheses and solicit feedback. Pertinently, during my last job search I had a couple of interviews that concluded with the assessment that my actual coding was the weakest part of my skillset. This was a bit of a shock, but my father pointed out that my desire to no longer code for a living had probably led to an ambivalence about the latest techniques, patterns and idioms in the tools I was using at the time. Now that I'm coding for myself alone, I want to sharpen every facet of my approach—without killing my timelines!

Lightbox, then, is a vehicle for many things, not least a "rebirth" of the software developer side of myself—the software craftsman particular about tools, techniques, and technologies, but sensitive to the nature of software as a conversation between its creators and its users.

What follows is more technically specific—nerdier, if you will. It is more germane to a narrow audience of software engineers, and particularly those working on Apple's platforms. Feel free to bail out if your eyes start to glaze over!

When Lightbox was first, truly "born," I was primarily a C++ programmer. I spent some time contributing to the Pencil2d open source animation editor, written in C++ and Qt, focusing on the Mac build. Around this time I had written an MPEG-1 encoder using types from what is now libav, intending to embed it in Pencil2d instead of spawning ffmpeg as a child process, which was incredibly brittle and error-prone. Trying to understand more about native Mac processes, frameworks, and user interface controls (Qt redraws every element itself, or at least did back then, such that the default UI looked several years out of date), I began dabbling in Objective-C, which would lead to an opportunity to become an iOS developer circa 2010.

Somewhere along the way, I decided it was better to focus the initial version of Lightbox on a single platform, obviously selecting the one I used daily, and to use the native platform tools and APIs as much as possible. Thus was Lightbox recast as an application written in Objective-C.

Everything I wanted—and want—to do with Lightbox is achievable in Objective-C, but Apple announced Swift at WWDC 2014, and, after some early disinterest, I felt it started to become an interesting and powerful language around late 2016. We began migrating to Swift at my job at the time, and given where I was with Lightbox I felt it made enough sense to rewrite what I had so far.

While there is a huge volume of Objective-C in production, not least within Apple itself, and the language is not disappearing any time soon, my nous for Apple's corporate behavior said that Swift would get all the cool new toys first moving forward, and that I'd want to have access to that in building my application. That has proven to be a prescient choice, as Swift has become a really powerful and even enjoyable language to work in, three years later… and the toys sure have come!

I should mention that Lightbox migrated from being a Mac-first application at this time to mobile-first, specifically for the iPad, thanks to the launch of the iPad Pro and Pencil. On the Mac I had relied on tablet digitizers like those from Wacom for high-precision pointer input—position, tilt, rotation, pressure. Third party styluses existed for the iPad before the Pencil, but they either just simulated our fat-meatstick fingers through a capacitive nib (e.g. the Cosmonaut by Studio Neat), delivering a drawing experience not unlike using a dry-erase marker; tried to at least get the fat nib out of the way, visually, like the Adonit Jot and Jot Pro; or required a Bluetooth connection and the integration of vendor-specific software, subject to additional licensing terms, like the Pencil by fiftythree or Wacom's Intuos Creative Stylus—both now discontinued. The Pencil was a revelation, with native platform support, and now it's available on even the cheapest iPad mini.

I had tried to cobble together mobile drawing solutions before, with a laptop and small Wacom Intuos tablet, but the cabling and power requirements and awkward ergonomics (not to mention weight) made it unsatisfactory. Manufacturers like Wacom themselves tried to address this market with devices like the Cintiq Companion, but the choice of a fundamentally desktop-oriented operating system in Windows made it awkward to use in many ways, just like the Microsoft Surface tablets. With the iPad Pro + Pencil, Apple offered an ergonomic, elegant, portable drawing solution for the digital artist—and the boom in applications like Procreate, Paper by fiftythree, Tayasui Sketch, Linea and more appear to support that thesis.

[Yes, I am aware of Android tablets, but which ones are in significant use beyond the Samsung Galaxy Tab? The consumption-oriented Amazon Kindle Fires don't count, for the purposes of market research for a professional-grade drawing app. Ditto the Galaxy Note; that's waaaay down on the priority list, after Linux.]

Speaking of Linux, a few years ago I toyed with the idea of writing the core application logic in a more "portable" language, with only the UI layer being specific to the platform and employing native APIs. The idea was to make the application easier to port to additional platforms post-launch. I was enamored with Rust at the time (still am, really; hope to use it on a future web component), so… While I eventually abandoned that in favor of focusing on a single platform to validate my hypotheses, it did result in my tiny contribution to cocoa-rs (now deprecated) and my bug report for rust-bindgen.

I mentioned SwiftUI earlier. Apple previously had a set of similar, yet subtly different platform APIs for user interface development across the Mac, iOS, and Watch (if you care about that)—AppKit, UIKit, and WatchKit, respectively. (TV uses a variant of UIKit, now; it used to publish a horrifying subset of HTML!) While I have settled on the iPad as my initial development and launch platform, I absolutely still intend to bring Lightbox to the Mac—and someday Windows and Linux, but that requires another discussion about constraints and portability. Announced just this past summer, SwiftUI is both a unified set of APIs for all of Apple's platforms, and a significant shift from an imperative to a declarative approach. Importantly, it significantly updates Apple's developer tooling, giving us live preview right in the IDE, and ending the long-fractious "marriage" of Interface Builder and Xcode proper.

[Historical aside: Interface Builder used to be its own application, before being merged into Xcode with version 4.0. Xcode itself was the successor to Project Builder; back then, Apple had one application in which to design and "compile" your visual interface assets, producing .nib files—NextStep Interface Builder—and another in which you wrote code that, when compiled, would load and deserialize those NIBs at runtime. As you can imagine, this created a lot of crash opportunities.

There were interim improvements, such as the adoption of an XML-based description of interface objects, saved as .xib files, to replace the serialized binary state of .nibs; and a further enhancement to make the XML generation stable, such that merely looking at a .xib in the integrated Interface Builder view didn't rewrite it and generate spurious diffs. These were welcome, but really patches on an approach that had been outgrown. And yes, I have hand-edited .xib files many times. Never again!

Suffice to say that the idea of a tight code evaluation loop with immediate visual feedback, and the ability to visually edit your interface and see the generated code, while eliminating the runtime crash possibilities, made a lot of developers excited! Who cared if Microsoft had shipped that to its devs in 2007—and since scrapped it.

Sorry, the ex-Windows dev in me can't resist digging at now-fellow Apple devs who think our tooling is state-of-the-art!]

SwiftUI makes a functional reactive style of user interface development natively feasible on Apple's platforms, far better than RxSwift or ReactiveCocoa ever could, and more comprehensively than Facebook's React Native. Coupled with the newly-announced diffable data sources and compositional layout APIs for table and collection views (still in need of native SwiftUI versions), representing application state as a tree where each nested/sub-state node is transformed by a function returning a new sub-state and rendered in SwiftUI with zero side effects becomes increasingly feasible. The craft side of me ambitiously wants to try for that!

The good thing about choosing to adopt SwiftUI is that I hadn't written too much of the user interface yet, and that SwiftUI can embed and fully interoperate with UIKit controls—also necessary, since many essential facilities are not yet available in SwiftUI (Collection Views, Metal Layers/MetalKit, PencilKit). As with Swift itself, I can tell that SwiftUI is the future of Apple user interface development, and will receive future enhancements much more readily than UIKit.

One of the areas where I am torn, from a craft perspective, is on graphical rendering. The SwiftUI WWDC videos recommend adopting Metal (Apple's high-performance, hardware-accelerated graphics and parallel computing library) for your custom graphics; I'm extremely good with Core Graphics, Core Image, and Core Animation, so this would be a transition for me. I am coming to terms with shipping an initial version built with Core Graphics—or even punting on that and using Apple's PencilKit—and a Metal implementation in a future update. Craft vs compromise.

The process of arriving at the set of choices that define the project has been involved, serendipitous, but also whimsical. Free of imposed deadline, but constrained by the desire to bring something real into the world. In a sense, I probably could have written earlier, lesser versions of Lightbox several times over in the time I have spent "finding its way," but that is the paradox of the passion project: I don't just want to put "something" out there; I want to put something great out there.

A certain measure of rewriting is inevitable, but that is true of all software. Post-launch, new platform features and constraints, new APIs and user expectations mean extending, modifying, exhuming, and often discarding substantial bits of code. The challenge for me as a developer is how to minimize those disruptions while maximizing my output. The challenge for me as an indie businessman is how to minimize time-to-market while maximizing customer satisfaction. The challenge for me as a human being is how to balance all this against my commitment to my family, my responsibility to my day job, and my availability to my friends and loved ones.

It's a glorious puzzle, itself a meta-layer of craft. And I love it!