Thursday, June 07, 2018

Wrists & Apprentices



I've been working with apprentices since the early 90s, but I haven't really written about it here; I suppose it's about time!

Every year or so my apprentice graduates and I hire a new one. I recently went through that hiring process, and I'm always a little overwhelmed by all of the interested (and interesting!) people. It's distressing that I can only hire one, especially since I seem to be one of the few people offering this sort of thing in the web development industry.

Anyway, a little history… I started programming when I was 9 and went pro when I was 17. I became obsessed and didn't take care of my body, and by the time I was 20 I had developed a chronic wrist injury that I still have decades later. Maybe I should have walked away from computers, but by then I was hooked! That was a dark time, but eventually I struck upon a solution… I could dictate my code to someone else who could be my hands. Turns out this can be very effective and rewarding (even if it does require some patience).

I use the term apprentice because they're never just a typist… It's impossible to be the conduit for all of that code without picking up a lot of things along the way. I liken it to learning a foreign language by going to a foreign country and hearing people speak it all day long. As we work, I try to explain things along the way, and encourage my apprentice to ask questions. At the beginning I may have to spell everything out, but over time the apprentice picks up the patterns and I can speak at a more high level. The more they understand of what we're doing, the more powerful a team we become!

When I started doing this in the early 90s, pair programming wasn't a widely known practice, but nowadays it's much more common. What I'm doing with my apprentice is much like pair programming, except that the experience gap between my apprentice and me is wider than for most pairs (sometimes my apprentices start out knowing nothing about programming, though that's rarer these days with all the online code learning resources), and we never swap who's typing. Many of the benefits to pair programming still apply:
  • Two pairs of eyes on the code means fewer mistakes
  • We are focused on the job all day long; having someone else there makes it harder to procrastinate
  • Knowledge exchange; my apprentice is learning from me, and I'm learning from talking things through with them
  • We are able to discuss design challenges and alternatives to come up with better solutions
You might think all of this communication must slow us down, but quite the contrary… When we get rolling we really fly! Two minds are better than one.
It's a very close working relationship… We sit just a few feet apart and talk constantly together all day long, week after week. Many of my former apprentices have become lifelong friends of mine. It's also very satisfying to see their careers develop after they move on to the next thing.

Often people ask me about my "apprenticeship program" and how they can replicate it at their company or in their region. First off: yes, you should! There are tons of promising novice programmers out there who just need a bit of guidance and an opportunity to grow. There seems to be no shortage of software jobs, but it's difficult for someone new to break in. Anyone who can tap into that budding talent gets a competitive advantage. Meanwhile, the apprentices get a head start with invaluable learning by being directly involved with real projects. Better training and knowledge exchange means better developers, which means a better industry. For more thoughts on why (and how), see Software Training Sucks: Why We Need to Roll it Back 1,000 Years from Rob Walling. I've also written about the benefits of apprenticeship culture before.

I think what I've been doing gives pretty good results, but of course it's also tuned to my situation… After all, the driving need here is for me to be able to get my work done. If I didn't need them to type for me, I would want the apprentice to move to greater levels of individual contribution gradually over time. As it is, I do recommend my apprentice use their off time to focus on their own projects and bring them in to discuss with me, to help cement their learning.

Anyway, my recommendations:
  • Pick someone who shows promise and passion but doesn't have a lot of experience. Don't just go for college students on break (like many internship programs), or you'll miss out on all the folks coming out of boot camps or who've learned with online resources. 
  • Get them working on real projects right away (whether this be in a company setting or on open source). Real projects are the only way to turn theory into skill. The sooner they get started, the sooner all of the lessons and exercises they've done will actually make sense. Besides, this way they're being productive from the beginning.
  • Start them out pairing with someone who's already established. Make sure the mentor/apprentice relationship is explicit and that both sides understand what's expected. I think it's good for the less experienced programmer to do the typing, because otherwise it's easy for them to zone out while the more experienced programmer whizzes around, but your mileage may vary. It may feel slow at first, but you're sharing valuable information, and things will speed up as your shared experience grows. 
  • Next step would be to have them make their own pull requests and have the experienced programmer critique them. This exercises different parts of the brain than the pair programming, and helps them move into the driver seat. Actually, I suppose you could do this step live, with the more experienced programmer doing the typing based entirely on dictation from the apprentice, but giving feedback along the way. Either way, you'll already have a rich shared understanding to build on.
  • Move them gradually up to more levels of autonomy, until one day they become the mentor for the next apprentice!
So that's my story, and some thoughts on bringing apprenticeship to your neck of the woods. I know it's already happening in some parts of the industry (within companies and at places like Apprenti), but I think it would be great for there to be more of it! Any skilled trade with a complex set of tools and a rich culture can benefit from the kind of cross-pollination and dialogue engendered by apprenticeship, and I'd say software development certainly fits the bill.

What do you think? I'm sure I've just scratched the surface… Hit me up with questions if you've got 'em!

Labels:


Thursday, February 08, 2018

2017



Wow, I guess I haven't been posting here much lately (though I've been posting regular updates on Patreon). Maybe it's time to change that… How about I start with a little 2017 wrap-up?

Let's see… I'd say it's been a pretty good year for me, both personally and professionally (politics notwithstanding), but I don't have anything really dramatic to report… It's mostly been a continuation of threads from previous years.

I'm working on a variety of projects professionally, as well as my various side projects. For the latter, my year-end wrap-up on Patreon is a good summary, and for the former, you can see a few new entries on my projects page, but a bunch of them are still under wraps.

We didn't go on any big trips, beyond going down to Oregon for the solar eclipse (which was amazing; highly recommended!). Caitlyn is continuing with her homeschool, and continuing to develop her skills in art (traditional and digital; 2D and 3D; still and animated). Christina is continuing to pursue her many pursuits and still somehow finding time to keep the rest of us organized.

Meanwhile, I read books, watched movies, and listened to music.

All in all it's been a fine year. What do I hope for from 2018? Well, probably more of the same. We've found a nice groove. Still, if I could have some wishes:
  • I'd like to do more writing. Maybe here, maybe on Patreon, maybe somewhere else… There are too many options these days, and it's not clear how best to find the right audience.
  • I'd like to get more people involved in my passion projects… Things like Arty Widget, Clockwork Goldfish, and Driftory are all made for people to build with; I need to encourage more people to do so.
  • I'd like to get involved in building VR experiences. Caitlyn and I have been playing in VR, there's a ton of VR stuff happening in this area, and I've been learning a lot about 3D programming, so I figure the time is right.
We shall see… May 2018 be a good year, for all of us!


Sunday, July 16, 2017

OpenSeadragon 2.3.0

We've just released version 2.3.0 of OpenSeadragon!

For those unfamiliar, OpenSeadragon is a web-based zooming library, used all over the world to share high-resolution imagery (classic paintings, ancient manuscripts, medical imaging, etc.). I wrote the original version back in 2007 (holy cow, that's 10 years ago!) when I worked on the Seadragon team at Microsoft. We later open sourced it, and it's been picking up steam ever since then.

This new version brings lots of little improvements along with new features like:
  • Zoomify format support. This is useful for moving over from legacy Zoomify installations, but it also gives us access to Zoomify's tileset creation tools, such as the Photoshop export plug-in, which may mean an easier on-ramp for folks new to zooming technology.
  • The ability to rotate individual images. You've already been able to rotate the entire viewport, but now you can rotate each image within individually.
  • Events to let you know when the current view has fully resolved; as the user zooms deep into your image it can take a moment for all the pixels to load, and knowing exactly when can be useful for certain effects.
  • The ability to preload images before showing them, for smoother transitions from image to image.
  • CommonJS support.
… and much more!

OpenSeadragon is truly a group effort, with 89 contributors so far, not to mention the folks creating plug-ins for it. I'm still kind of stunned at how the OpenSeadragon community has grown!

I oversee the project, but my role is more facilitator than lead. Whenever someone reports an issue, I help them get to the bottom of it, and I review their patches, but I don't do the actual coding for them. This allows me to have a greater impact with the limited time I have for the project, and it empowers more people to become actively involved in shaping the code. I think if I'd personally taken on fixing every bug and adding every feature as they are posted, I would have burned out long ago, and none of the great contributors would have joined.

So, if you're new to OpenSeadragon, come check it out and see all the cool stuff people have made with it! Also, if you appreciate my part in the project, please consider pledging to my Patreon to support my work there.

Congratulations to everyone who's made this release possible! Here's to many more years of awesome zooming!

Sunday, November 06, 2016

Getting to the Bottom of It

In today's world we have many large controversial topics that we keep going around and around on, instead of just getting to the bottom of. I'm sure there are many reasons for this, many of which don't have anything to do with rational discourse, but let's pretend for a moment that a little rational discourse can at least make a small improvement.

Part of the problem with these big topics is that they are so multifaceted, it's hard to hold it all in your head at once. What we need is a big map starting from a simple premise and going down, down, down through all the pros and cons, twists and turns, branching into every subtopic, to fully cover every facet. I'm confident this information is already available; it just needs to be organized.

I'm imagining a website for this. Anyone can start a new topic, e.g. "is global warming real?" Then anyone can add pro or con posts, and anyone can add rebuttals to each, and rebut the rebuttals and so on. Once a topic is in place, you can navigate it by expanding the areas that interest you, until you get to the bottom of it.

In order for this to work, a lot of care needs to go into making sure the structure is clean… No circular arguments, no logical fallacies. All of the misinformation needs to be debunked (not just buried). Wherever possible there should be links to primary sources. Each answer needs to cover a single point. This means there needs to be as much energy and facility directed toward structural work as there is toward content contribution.

So, for instance, redundant answers need to be able to be merged; answers covering multiple points need to be split; points that have been covered elsewhere need to be linked; logical fallacies need to be flagged; etc. Of course each of these actions need to be vettable, so improperly merged, split, or flagged items can be undone. All of this activity needs to be transparent.

Even if a thing like this won't resolve these arguments directly, it would be an invaluable resource for those working to do so.

Labels:


Saturday, April 30, 2016

Captain America and the Planetary Era

The upcoming Captain America movie has reminded me of a thing I wrote up about Winter Soldier back when it came out but never got around to posting, about the way Planetary Era themes popped up in that seemingly unlikely place.

If you're not familiar with the Planetary Era concept, I highly recommend watching my father's talk: What Time Is It? The basic idea is that various global trends are moving us into a new societal state that is fundamentally different than the empire-driven societies of the last few millennia. Due to our now globally interconnected nature we need to start thinking and working together as one planetary family, with all of the opportunities and challenges that entails. As with any major shift, some folks are already way ahead on this front, while others lag behind.

So anyway, here are my thoughts. If you haven't already seen Winter Soldier, you need to re-examine your priorities, but also be warned spoilers lie below:

So I saw Captain America: Winter Soldier, and I was struck by a couple of Planetary Era themes. I know, you'd think a movie featuring a hero named after a country would be all about Empire Era thinking, and there was plenty that too, but you could see the Planetary Era peeking through.

One thing that really stuck out for me was the way in which the conflict was repeatedly framed as one between order and chaos. That's not unusual in movies of this sort, but what is unusual is that it was pretty clearly stated that order was the side of the villains, not the good guys. Now, of course any healthy society has a balance between order and chaos, but balance isn't a static thing; it's always moving to match the needs of the time. While the Planetary Era may in fact be more orderly in some ways, I'd say it's pretty clear that the Planetary Era themes of diversity, self-organization, and even our increasingly small-world connectivity will certainly look and feel more chaotic to many observers. Rather than clinging to the illusion of order the Empire Era thinking provided, we will be better served by stepping into the apparent chaos of a vibrant interconnected world.

The other thing that struck me was how one of the key aspects of the good guys winning was the public sharing of all of SHIELD's secrets. This too seems more in tune with the Planetary Era's themes of connectivity and self-organization than the Empire Era's rigid hierarchies and us-versus-them thinking.

Perhaps it's a bit much to say that Captain America is leading the charge into the Planetary Era, but it's good to see that even in this unlikely place, ideas that resonate with the new era are taking hold. I'm looking forward to the upcoming movie… We'll see if it continues the trend, or if Winter Soldier was just a comic book outlier.

Labels: ,


Sunday, November 29, 2015

The Importance of Being Ironic

Hipsters and their irony, amirite? What even is the deal?

In the 20th century, the world got way smaller than ever before, but now in the 21st we've taken it to a whole new level with almost instant access from anyone to anyone on the planet. 100 years ago you could easily go months or even years without your worldview being challenged. Not so much anymore, even with the filter bubble effect.

This is actually a good thing… The world has been filled with different ideas and viewpoints for all of history, and it's about time we as a species get around to accepting this fact. It's about time we learned to use our differences to the benefit of all. Just as in natural ecosystems, diversity creates strength, but it also has its challenges.

One of these challenges is holding conflicting ideas in mind simultaneously without feeling the need to pick sides. Sometimes the only way through a complex situation lies on the road to seeming paradox. Becoming comfortable with this cognitive dissonance, so you can freely navigate the multilayered thoughtspace of the world, is a key skill of the 21st century.

And irony, our old friend, is a handy steppingstone to mastery of that skill. What is irony, after all, but the binding together of conflicting ideas? Whether they know it or not, irony aficionados are doing exactly what they must in order to thrive in the hyper-connected world of today and tomorrow.

Labels:


Wednesday, May 27, 2015

OpenSeadragon 2.0: Forest-to-Tree Zooming

Today we cross a major threshold!

OpenSeadragon turns 2.0 and brings with it full support for zooming multiple high-resolution images in the same scene. Imagine all the pages of an ancient manuscript laid out on your screen and being able to seamlessly zoom in to the smallest detail on any page.

Back in 2008 when OpenSeadragon (then called Seadragon Ajax) was first released — when IE 7 was Microsoft's top browser and woolly mammoths wandered the frozen steppes — smoothly zooming a single image in pure JavaScript was quite the feat! We could only dream about the sort of cool multi-image scenarios Blaise demoed using a native app with GPU-accelerated code in his famous TED talk.

OpenSeadragon doesn't yet do everything shown in that video, but multi-image opens up some exciting new possibilities! Check out these demos for some inspiration:



OpenSeadragon + Rdio: swim across a sea of high-resolution album artwork, discovering new music as you go.




OpenSeadragon + Flickr: hop from image to tag to image on Flickr's most interesting photos of the day.




Chris Jordan Infinite Zoom: kick back and fill your screen with Chris Jordan's giant photo montages.


If you're new to OpenSeadragon, you should also check out all the great work people have done with the single-image version, such as this gigantic panorama of Mont Blanc and this amazingly detailed story-map of Central America.

Now what are you waiting for? Make some cool multi-image OpenSeadragon things!

Labels: ,


Tuesday, February 03, 2015

self = this

I teach JavaScript, and one of the things I've found most confuses people is the self = this pattern (some people even go to great lengths to avoid learning about it). I haven't been able to find a good post on it, so here's my attempt. If you're not interested in JavaScript, feel free to skip this and read some of my other fine posts.

Okay, I'll start with a concise definition and then explain in depth.

Use the self = this pattern whenever you have a function nested inside of an object's method, and you want to access the object's properties.

Assign self in the object's method and use it inside the nested function. Here's how it looks:

var myObject = {
  aMethod: function() {
    var self = this;

    this.aProperty = 'foo';

    setTimeout(function() {
      console.log(self.aProperty); // outputs 'foo'
    }, 1);
  }
};

So, let's explain further.

Objects with Functions

While you can certainly write plenty of JavaScript without ever attaching a function to an object, I find that organizing my code into objects makes things a lot cleaner. To avoid junking up the global namespace, I usually define a single global object (named "App" or some such) that all of my variables and functions are attached to. To further organize, I'll group related functions and variables into sub-objects attached to the main object.

Most objects will have a mix of variables (called properties when they are part of an object) and functions (called methods when they are part of an object). Here's a simple object with one of each:

var myObject = {
  aProperty: 'foo', 
  aMethod: function() {
  }
};

Inside an object's methods, there's a special variable, named this, that is a reference to that object. If the method wants to access any of the object's properties or other methods, it does so through that variable, like so:

var myObject = {
  aProperty: 'foo', 
  aMethod: function() {
    console.log(this.aProperty); // outputs 'foo'
  }
};

This special variable is managed by JavaScript; you don't have to assign it, and in fact you can't. It's assigned automatically for every function. If it's an object method, this is a reference to the object. For any other object, this is a reference to the global variable namespace.

Nested Functions

In JavaScript, you can nest functions inside of other functions, including object methods. This is a common practice when working with user events, network requests, timers, etc.

var myObject = {
  aMethod: function() {
    setTimeout(function() {
    }, 1);
  }
};

One handy feature of nested functions is that the inner function has access to all of the variables defined in the outer function. This is called closure.

var myObject = {
  aMethod: function() {
    var a = 'foo';

    setTimeout(function() {
      console.log(a); // outputs 'foo'
    }, 1);
  }
};

The one exception is the special this variable; since the nested function is just a regular function, not an object method, its this is a reference to the global namespace.

var aProperty = 'global';

var myObject = {
  aMethod: function() {
    this.aProperty = 'local';
    
    setTimeout(function() {
      console.log(this.aProperty); // outputs 'global'
    }, 1);
  }
};

Putting It All Together

So, we need a way to get a reference to the object to be available inside the nested function. Closure to the rescue! We assign another (non-special) variable, e.g. self, with the object from this. Since this variable isn't overwritten for each function (unlike this), we can use it inside the inner function.

var myObject = {
  aMethod: function() {
    var self = this;

    this.aProperty = 'foo';

    setTimeout(function() {
      console.log(self.aProperty); // outputs 'foo'
    }, 1);
  }
};

Notes

  • There's nothing magic about the word self; you can use whatever variable name you want. However, self is the most commonly used variable name for this pattern, so it's a good choice for clarity's sake.
  • If a method needs a var self = this; line, I always put it at the very top of the method. Putting it at the top isn't absolutely necessary, but I think it's a good convention; that way you don't have to go hunting to see if it's defined.
  • this is only set up properly if you call the method via the object, like myObject.aMethod(). If you instead call the method indirectly, such as when you pass it to another function (e.g. setTimeout(myObject.aMethod, 1)), this won't be set up. To deal with this, we use closures again, like so: setTimeout(function() { myObject.aMethod(); }, 1).
  • It's actually possible in certain cases for this to not be the object or the global namespace. By using JavaScript's apply or call features, you can set this to whatever you want when you're calling a function. jQuery uses this in its event handlers, for instance.

Tuesday, January 06, 2015

Be More Awesome

With education, as with many things, I find it's useful to start from basic principles. There are so many options and philosophies, it's easy to get overwhelmed. A good mission statement can cut through all that, or at least give you a place to start.

So, here's ours:

The goal is for Caitlyn to become more awesome. Our strategy for becoming more awesome is to learn new skills and improve existing ones. The only way to learn/improve skills is through practice. Therefore her job is to practice, and to improve her ability to practice (since practicing is itself also a skill).

Practice can be made more effective with:

  • Instruction and examples
  • Feedback
  • The proper tools
  • A conducive environment
  • Goals and rewards
  • Other skills
  • No doubt many more things

All of these are worth gathering when possible, but none of them are worth waiting for.

That's it.

I suppose one other question worth answering is, "Which skills should we practice?" To be honest, I don't think it really matters that much. All skills are useful, and all can be translated to other contexts, and at any rate the most important skill is how to practice effectively. That said, some of the most important skills are what you might call building block skills; those that many other skills are based on. Reading, writing, and math are the obvious ones, but there are many more, such as time management, teamwork, research, and experimentation.

Anyway, with this simple mission statement, we can focus on what's important while allowing a broad range of variation.

Labels: ,


Tuesday, October 21, 2014

How I Rdio

I've been a fan of Rdio for years now (which, come to think of it, is probably why I work for them). More interesting, though, than the question, “What music service do you use?” is, “How do you actually use it?” Here then, is my answer. For starters, it should be noted that while in general I love Rdio’s UI, there are various things I’ve found to be missing that I’ve gone ahead and built for myself using Rdio’s lovely JavaScript API (which, as it turns out, I also built; that’s how I know how lovely it is). With that in mind, let’s take a tour:

The Tunes Must Flow

I listen to music most frequently while working at my computer. During the workday, I just want to have excellent music playing, and I want to stay in the flow. For me this means whole albums, because each album has a consistency that doesn’t grab your attention with each track change. This also means being able to queue up a whole bunch of albums ahead of time, so I don’t have to stop and fiddle with my music periodically throughout the day. Fortunately, Rdio excels at thinking in terms of albums, and it’s got a fine queue. Every so often, when the queue runs low, I’ll jam a new pile on; I’ve generally got dozens of albums lined up.

Discovery


When I’m not working, I do like to fiddle with music, as a leisure-time activity. In the evenings I sometimes wander around, checking things out, adding new stuff to my queue or favorites, etc. This is the modern-day equivalent of my old habit of haunting music stores, looking for that next great find. If I'm looking for new music, I'll use Fathom or the social stuff that shows up on Rdio’s homepage feed or the "here are new albums from your favorite artists" feature that shows up in notifications.

Once I’ve found a promising-sounding album, I toss it on the queue for a full listening during the workday. Every few days I go to Music Triage which shows me any new albums I've listened to that I haven't already triaged. Here I quickly add things to my favorites or mark things I didn't like. In this way I make sure nothing falls through the cracks, without having to pay attention throughout the day.

Rediscovery

It turns out music discovery these days is so great, I’ve got a new problem: hundreds of albums I’ve discovered and liked enough to add to my favorites but never listened to again because I was off to the next big thing. To listen to music I already know I like, and hopefully connect more deeply with things I’ve discovered, I pull up Collection Random and use it to quickly queue up a bunch of albums. I find this "show me some stuff randomly from my collection" better than any sort of rational sorting… otherwise you end up falling into ruts.

Mid-Queue Interruptions

My next-most-frequent music-listening station is in the kitchen while cooking and cleaning. I usually want to listen to something entirely different there; more upbeat and attention-grabbing. If I think of it before leaving my computer, I push an appropriate album ahead of the queue (in Rdio’s Mac interface, it’s command-clicking the play button on any album). Unfortunately, Rdio’s mobile app doesn’t support such things, so I’ve made Mobile Queue to support reorganizing the queue on my mobile web browser.

So there you have it

…my music “workflow”. Who knows what my music rhythm will be like in a year, but for now this is working great, and I love having the power to customize my interactions as needed.  At the moment, I think I’ve found a good balance between finding new stuff and listening to favorites, with a minimum of ongoing effort (not counting the initial development time, which was fun noodling anyway).

How about you? How do you use your music service?

Labels: