Thoughts on tools, design, and feedback loops

I just watched Inventing on Principle from Bret Victor and found this entire talk incredibly interesting. Chris Granger’s post on Light Table led me to this, and shortly after, I found the redesigned Khan Academy CS course which this inspired. Bret touched on something that basically anyone who’s attempted to design anything has implicitly understood: This feedback loop is the most essential part of the process.

I reflected on this and on my own experiences, and decided on a few things:

(1) Making that feedback loop fast enough can dramatically change the design process, not just speed it up proportionally.

I feel that Bret’s video demonstrates this wonderfully. It matches up with something I’ve believed for awhile: That a slower, more delay-prone process becoming fast enough to be interactive can change the entire way a user relates to it. The change, for me at least, can be as dramatic as between filling out paperwork and having a face-to-face conversation. This metamorphosis is where I see a tool become an extension of the mind.

Toplap probably has something to say on this. They link to a [short] live coding documentary, Show Us Your Screens. I rather like their quote: “Live coding is not about tools. Algorithms are thoughts. Chainsaws are tools. That’s why algorithms are sometimes harder to notice than chainsaws.”

Live coding perhaps hits many of Bret’s points from the angle of musical performance meeting programming. Since he spoke directly of improvisation, I’d say he was well aware of this connection.

(2) These dynamic, interactive, high-level tools don’t waste computer resources – they trade them.

They trade them for being dynamic, interactive, and high-level, and this very often means that they trade ever-increasing computer resources to earn some ever-limited human resources like time, comprehension, and attention.

I don’t look at them as being resource-inefficient. I look at them as being the wrong tool for those situations where I have no spare computer resources to trade. Frankly, those situations are exceedingly rare. (And my degree is in electrical engineering. Most coding I’ve done when acting as a EE guy, I’ve done with the implicit assumption that no other type of situation existed.) Even if I eventually have to produce something for such a situation – say, to target a microcontroller – I still have ever-increasing computer resources at my disposal, and I can utilize these to great benefit for some prototyping.

Limited computer resources restrict an implementation. Limited human resources, like time and attention and comprehension, do the same…

(3) The choice of tools defines what ideas are expressible.

Any Turing-complete language can express a given algorithm, pretty much by definition. However, since this expression can vary greatly in length and in conciseness, this is really only of theoretical interest if you, a human, have only finite time on earth to make this expression and only so many usable hours per day. (This is close to a point Paul Graham is quite fond of making.)

This same principle goes for all other sorts of expressions and interactions and interfaces, non-Turing-complete included, anytime different tools are capable of producing the same result given enough work. (I can use a text editor to generate music by making PCM samples by hand. I can use a program to make an algorithm to do the same. I can use a program such as Ableton Live to do the same. These all can produce sound, but some of them are a path of insurmountable complexity depending on what sort of sound I want.)

In a strict way, the choice of tools defines the minimum size of an expression of an idea, and how comprehensible and difficult this expression is. Once this expression hits a certain level of complexity, a couple paths emerge: it may as well be impossible to implement, or it may cease to be about the idea and instead be an implementation of a set of ad-hoc tools to eventually implement that idea. (Greenspun’s tenth rule, dated as it is, indicates plenty of other people have observed this.)

In a less strict way, the choice of tools also guides how a person expresses an idea; not like a fence, but more like a wind. It guides how that person thinks.

The boundaries that restrict time and effort also draw the lines that divide ideas into possible and impossible. Tools can move those lines. The right tools solve the irrelevant problems, and guide the user into solving relevant problems instead.

Of course, finding the relevant problems can be tricky…

(4) When exploring, you are going to re-implement ideas. Get over it.

Turning an idea plus a bad implementation into a good implementation, on the whole, is far easier than turning just an idea into any implementation (and pages upon pages of design documentation rarely push it past ‘just an idea’). It’s not an excuse to willingly make bad design decisions – it’s an acknowledgement that a tangible form of an idea does far more to clarify and refine those design decisions than any amounts of verbal descriptions and diagrams and discussions. Even if that prototype is scrapped in its entirety, the insight and experiences it gives is not.

The flip side of this is: Ideas are fluid, and this is good. Combined with the second point, it’s more along the lines of: Ideas are fluid, provided they already have something to flow from.

A high-level expression with the right set of primitives is a description that translates very readily to other forms. The key here is not what language or tool it is, but that it supports the right vocabulary to express the implementation concisely. Supports doesn’t mean that it has all the needed high-level constructs – just that it is sufficiently flexible and concise to build them readily. (If you ‘hide’ higher-level structure inside lower-level details, you’ve added extra complexity. If you abuse higher-level constructs that hide simpler relationships, you’ve done the same. More on that in another post…)

My beloved C language, for instance, gives some freedom to build a lot of constructs, but mainly those constructs that still map closely to assembly language and to hardware. C++ tries a little harder, but I feel like those constructs quickly hit the point of appalling, fragile ugliness. Languages like Lisp, Scheme, Clojure, Scala, and probably Haskell (I don’t know yet, I haven’t attempted to master it) are fairly well unmatched in the flexibility they give you. However, in light of Bret’s video, the way these are all meant to be programmed still can fall quite short.

I love Context Free as well. I like it because its relative speed combined with some marvelous simplicity gives me the ability to quickly put together complex fractalian/mathematical/algorithmic images. Normal behavior when I work with this program is to generate several hundred images in the course of an hour, refining each one from the last. Another big reason it appeals to me is that, due to its simplicity, I could fairly easily take the Context Free description of any of these images and turn it into some other algorithmic representation (such as a recursive function call to draw some primitives, written in something like Processing or openFrameworks or HTML5 Canvas or OpenGL).

 

My experiences with Apache Axis2/C

(This is an abridged version of a report I did at my job; I might post of copy of it once I remove anything that might be considered proprietary.)

I was tasked at my job with looking at ways of doing web services in our main application (which for an upcoming delivery is to be separated out into client and server portions). Said application is written primarily in C++, so naturally our first look was into frameworks written for C or C++ so that we would not need to bother with language bindings, foreign function interfaces, porting, new runtimes, or anything of the sort.

Our search led us to Apache Axis2/C. We’d examined this last year at a basic level and found that it looked suitable. Its primary intended purpose was as the framework that the client and server communicated over in order to transfer our various DTOs; that it worked over SOAP and handled most HTTP details (so it appeared) was a bonus.

I discovered after investing considerable effort that we were quite wrong about Axis2/C. I’ll enumerate a partial list of issues here:

  1. Lack of support: There was a distinct lack of good information online. I could find no real record of anyone using this framework in production anywhere. Mailing lists and message forums seemed nonexistent. I found a number of articles that were often pretty well-written, but almost invariably by WSO2 employees.
  2. Development is largely stagnant: The last update was in 2009. In and of itself this is not a critical issue, but combined with its extensive list of unsolved bugs and a very dense, undocumented code base, this is unacceptable.
  3. Lack of documentation: Some documentation is online, but the vast majority of the extensive API lacks any documentation, whether a formal reference or a set of examples. The most troubling aspect of this is that not even the developers of Axis2/C seemed to comprehend its memory management (and indeed our own tests showed some extensive memory leaks).
  4. Large set of outstanding bugs: When I encountered the bug-tracking website for Axis2/C (which I seem to have lost the link for), I discovered a multitude of troubling bugs. Most of them pertain to unfixed memory leaks (for code that will be running inside of a web server, this is really not good). On top of this, a 2-year-old unfixed bug had broken the functionality for binary MTOM transfers if you had enabled libcurl support, and this feature was rather essential to the application.
  5. Necessity of repetitive code: It lacked any production-ready means to automatically generate code for turning native C/C++ objects to and from SOAP. While it had WSDL2C this still left considerable repetitive work for the programmer (in many cases causing more work rather than less) and its generated code was very ambiguous as to its memory-management habits.
  6. Limited webserver support: Axis2/C provided modules only for working with three web servers: Apache HTTPD, Microsoft IIS, and their built-in test server, axis2_http_server. Our intended target was Microsoft IIS, and the support for IIS was considerably less developed than the support for Apache HTTPD. To be honest, though, most of my woes came from Microsoft here – and the somewhat pathetic functionality for logging and configuration that IIS has. I’m sorry for anyone who loves IIS, but I should not be required to manually search through a dump of Windows system calls to determine that the reason for IIS silently failing is that I gave a 64-bit pool a 32-bit DLL, or that said DLL has unmet dependencies. Whether it’s Axis2/C’s fault or IIS’s fault that the ISAPI DLL managed to either take IIS down or leave it an an indeterminate state no less than a hundred times doesn’t much matter to me. (However, on the upside, I did learn that Process Monitor from Sysinternals can be very useful in cases where you have otherwise no real source of diagnostic information. This is not the first time I had to dump system calls to diagnose an Axis2/C problem.)
  7. Poor performance: Even the examples provided in the Axis2/C source code itself had a tendency to fail to work properly.
    1. Their MTOM transfer example failed to work at all with Microsoft IIS and had horrid performance with Apache HTTPD.
    2. On top of this, the default configuration of Apache Axis2/C opens up a new TCP connection for every single request that is initiated. Each TCP connection, of course, occupies a port on the client side. On Windows, something like 240 seconds (by default) must pass upon that connection closing before the port may be reused; on Linux, I think it’s 30 seconds. There are 16384 ports available for this purpose. Practical upshot of this: A client with the default configuration of Axis2/C cannot sustain more than 68 requests per second on Windows or 273 requests per second on Linux. If you exceed that rate, it will simply start failing. How did I eventually figure this out? By reading documentation carefully? By looking at an API reference? By looking at comments in the source code? No, by looking at a packet dump in Wireshark, which pointed out to me the steadily increasing port numbers and flagged that ports were being reused unexpectedly. I later found out that I needed to compile Axis2/C with libcurl support and then it would use a persistent HTTP connection (and also completely break MTOM support because of that unfixed bug I mentioned). None of this was documented anywhere, unless a cryptic mailing-list message from years ago counts.

So, I’m sorry, esteemed employees of WSO2, but to claim that Apache Axis2/C is enterprise ready is a horrid mockery of the term.

This concluded about 2 weeks of work on the matter. In approximately 6 hours (and I’ll add that my starting point was knowing nothing about the Java technologies), I had a nearly identical version using Java web services (JAX-WS particularly) that was performing on the order of twice as fast and with none of the issues with memory leaks or stability.

P.S. Is it unique to Windows-related forums that the pattern of support frequently goes like this?

  • Me: This software is messed up. It’s not behaving as it should.
  • Them: It’s not messed up; it works for me. You are just too dumb to use it. Try pressing this button, and it will work.
  • Me: Okay, I pressed it. It’s not working.
  • Them: Oh. Your software is messed up. You should fix it.

OpenFrameworks, try 1…

My attempts at doing things with OpenFrameworks on MacOS X have been mildly disastrous. This is a bit of a shame, because I was really starting to like OpenFrameworks and it was not tough to pick up after being familiar with Processing.

I’m pretty new to XCode, but it’s the “official” environment for OpenFrameworks on OS X, so it’s the first thing I tried. The first few attempts at things (whether built-in examples, or my own code) went just fine, but today I started trying some things that were a little more complex – i.e. saving the last 30 frames from the camera and using them for some filtering operations. My code probably had some mistakes in it, I’m sure, and that’s to be expected. The part where things became incredibly stupid was somewhere around when the mistakes caused the combination of XCode, GDB, and OpenFrameworks to hose the system in various ways.

First, it was the Dock taking between 15 and 30 seconds to respond just so I could force-quit the application. Then it was the debugger taking several seconds to do 100 iterations of a loop that had nothing more than an array member assignment inside of it (and it had 640x480x3 = 921,600 iterations total) if I tried to set breakpoints, thus basically making interactive debugging impossible. The debugging was already a pain in the ass; I had reduced some code down to something like this:

int size = cam_width * cam_height * 3;
for(int i = 0; i < frame_count; ++i) {
   unsigned char * blah = new unsigned char[size];
   for(int j = 0; j < size; ++j) blah[j] = 0;
}

…after a nearly identical memset call was smashing the stack and setting frame_count to a value in the billions, so I was really getting quite frazzled at this.

Running it a few minutes ago without breakpoints enabled led to a bunch of extreme sluggishness, then flickering and flashing on the monitor and I was not able to interact with anything in the GUI (which was the 3rd or 4th time this had happened today, with all the Code::Blocks nonsense below). I SSHed in from another machine and killed XCode, but the monitor just continued to show the same image, and it appeared that the GUI was completely unresponsive except for a mouse cursor. I had to hold the power button to reboot, and saw this in the Console but nothing else clear before it:

6/13/11 1:11:19 AM [0x0-0x24024].com.google.Chrome[295] [463:24587:11560062687119:ERROR:gpu_watchdog_thread.cc(236)] The GPU process hung. Terminating after 10000 ms.

A little before trying XCode for a 2nd time, I had also attempted to set up Code::Blocks since it’s OpenFrameworks’ “official” IDE for Linux and Windows and XCode was clearly having . First I painstakingly made it built from an SVN copy and finally got it to run (had to disable FileManager and NassiShneiderman plugins which would not build and make sure it was building for the same architecture as wxWidgets was built for). As soon as I tried to quit it, the Dock became totally unresponsive, then Finder itself followed along with the menu bar for the whole system. I was not able to SSH in. Despite the system being mostly responsive, I had to hard reset. I found a few things in the console:

6/12/11 9:43:54 PM com.apple.launchd[1] (com.apple.coreservicesd[66]) Job appears to have crashed: Segmentation fault
6/12/11 9:43:54 PM com.apple.audio.coreaudiod[163] coreaudiod: CarbonCore.framework: coreservicesd process died; attempting to reconnect but future use may result in erroneous behavior
6/12/11 9:43:55 PM com.apple.ReportCrash.Root[18181] 2011-06-12 21:43:55.011 ReportCrash[18181:2803] Saved crash report for coreservicesd[66] version ??? (???) to /Library/Logs/DiagnosticReports/coreservicesd_2011-06-12-214355_localhost.crash
6/12/11 9:44:26 PM com.apple.Dock.agent[173] Sun Jun 12 21:44:26 hodapple2.local Dock[173] <Error>: kCGErrorIllegalArgument: CGSSetWindowTransformsAtPlacement: Singular matrix at index 2: [0.000 0.000 0.000 0.000]

It started up properly after a reset, but I couldn’t do anything useful with it because despite there being a script that was supposed to take care of this while building the bundle the application was not able to see any of its plugins, which included a compiler plugin. I attempted a binary OS X release which had a functioning set of plugins, but was missing other dependencies set in the projects, which were Linux-specific. I could probably put together a working configuration if I worked in Code::Blocks a bit, but I have not tried yet.

This is all incredibly annoying. There is no reason a user process should be capable of taking down the whole system like this, especially inside of a debugger, yet apparently it’s pretty trivial to make this happen. I’ve written more than enough horrible code in various different environments (CUDA-GDB on a Tesla C1060, perhaps?) to know what to expect. I guess I can try developing on Linux instead, and/or using Processing. I know it’s not quite the same, but I’ve never had a Processing sketch hose the whole system at least.

Later addition (2011-06-20, but not written here until November because I’d buried the notes somewhere):

I attempted to make an OpenFrameworks project built with Qt Creator (which of course uses QMake). OpenFrameworks relies on QuickTime, and as it happens, QuickTime is 32-bit only. If you take a look at some of the headers, the majority of it is just #ifdef’ed away if you try to build 64-bit and this completely breaks the OpenFrameworks build.

Ordinarily, this would not be an issue as I would just do a 32-bit build of everything else too. However, QMake refuses to do a 32-bit build on OS X for some unknown reason (and, yes, I talked to some Qt devs about this). It’ll gladly do it on most other platforms, but not on OS X. Now, GCC has no problems building 32-bit, but this does no good when QMake keeps adding “-arch x86_64″ no matter what. I attempted all sorts of options such as CONFIG += x86, CONFIG -= x86_64, QMAKE_CXXFLAGS -= -arch x86_64, or  += -m32, or += -arch i386… but none of them to any avail.

I can never win that context back

I stumbled upon this: http://www.soyoucode.com/2011/coding-giant-under-microscope-farbrausch . . . and promptly fell in love with the demos there from Farbrausch:

.the .product

.debris

.kkrieger

Magellan

That melding of music and animated 3D graphics grabs a hold of me like nothing else. I don’t really know why.

The fact that it’s done in such a small space (e.g. 64 KB for the first one) makes it more impressive, of course. Maybe that should be a sad reflection on just how formulaic the things I like are, if they’re encoded that small (although, that ignores just how much is present in addition,  in the CPU and the GPU and the OS and the drivers and in the design of the computer), but I don’t much care – formulas encode patterns of sorts, and we’re pattern-matching machines.

But leaving aside the huge programming feat of making all this fit in such a small space, I still find it really impressive.

It’s been a goal for awhile to make something that is on the scope of that (highly-compressed demo or not, I don’t much care). I’ve just not made much progress to accomplishing that. My early attempts at Acidity were motivated by the same feelings that draw me to things like this.

(Obligatory Second Reality as well. Maybe I am putting myself too much in the context that it came from – i.e. 1993 and rather slow DOS machines – but I still think it’s damn impressive. Incidentally, this is also one of the only ones I’ve run on real hardware before, since apparently the only fast machine I have that runs Windows is my work computer.)

Angry Rants, 2010 Summer edition

Apparently I have written a lot of angry rants this quarter while I was supposed to be studying. Since I’m throwing the paper away anyhow, I’ll go ahead and put its contents here:

1. If its creators pay it little respect, do I really owe it any respect beyond this?


2. Every hour I work in the world of numbers, algorithms, and electronics convinces me further of the value of humans.


3. His refusal to play your game does not mean he is stupid. The fact that he is playing some other game does not mean he is stupid. His neglect of what is a high priority for you does not mean he is stupid. His focus is a separate context from your chosen context does not mean he is stupid. His rank in your game does not mean he is stupid. 99% of your definitions of “stupid” consist of defining part of you as a standard and then labeling everything else that falls short. Why not, instead of dismissing it all, you pull your head out of your ass and learn how people actually behave?

I don’t want you to regard all people as equal and correct in their own way or all with equally valid viewpoints or engage in any other pointless exercise in vague relativism. I want you, when you think you’re surrounded by idiots, to take a good, hard look at how contrived and confused your own definition of “idiot” is, instead of just putting up another brick in the wall that closes off your mind further.


4. Your game is a game, not reality. Go ahead and judge people on their ability to play, but recognize it for what it is instead of insisting that the game is some sort of universal truth.


5. Use abrasive speech that wears through a person’s ignorance, and expect a backlash as though you’d shredded that person’s clothes off.


6. It’s pretty straightforward. When I see you judge aperson by one facet of their behavior, you have in your mind turned that person into a tiny fraction of one.

I can’t expect you to not have instincts or emotions, but I can expect you to carry about you a self-awareness that turns you from an instinctive, emotional animal into a rational human.


7. My realm of familiarity is a dead end.


8. Moment of clarity, drowned in a sea of distraction.

No, I have no idea what I meant.