When I tell folks how long I’ve been doing JavaScript development, I use the phrase

… oh, since before the Turn of the Century.

You know, all casual-like. Sometimes I’ll also throw in the phrase “high-order JavaScript”, just to shine one on.

Yet, in going back over my blog posts during a recent refactoring, I was surprised to find how few of them reflected my long and storied history with the language.

I was inspired to correct this gross oversight.

A Brief Aside

Something deep within me insisted on making this Post into a confessional as well. I felt myself compelled to be achingly, if not brutally, up-front about many (though not all[1]) non-technical challenges I’ve encountered along my path.

I found it hard to retrace my “entire” career without revealing too these junctures which have led to sudden, unexpected growth in my professional character. Upon reflection, it seems this Post was destined to be not only a timeline, but also an exercise in personal accountability.

So; read on and witness my tale with all of its wounds and bruises and closures; or TL;DR it and just jump to things as of Today.

JavaScript Bandwagon

I had no idea know when I’d started putzing around with my first website that I’d be hitching my horse to JavaScript for decades to come. In many ways, I resisted it. I really enjoy writing in the language – much as I do with Ruby – and JavaScript’s ubiquity has made it the de facto language of the Web.

It was inspiring to read mr. Crockford’s opinionated wisdom beamed down from high upon Yahoo headquarters, but even with the potential of Rhino, for a long time the language’s impact seemed to be contained only to the browser. My preference felt like a constraining choice, limiting me to the Front End of the stack.

Then was born the Node.js wrapper around libuv and a revelation of how well JavaScript’s scope-capturing single-threaded features lend themselves towards efficient non-blocking I/O. In a great Cambrian explosion, JavaScript became a full stack language, and today[2] it thrives.

Despite all the WAT inherent in mr. Eich’s invention, it seems to hold boundless potential, inspiring the nasence of WebAssembly via emscripten to polemnics about releasing Zalgo and the phenomenon of JavaScript Fatigue Fatigue.

So, now that I have firmly and decisively taken the reins of my Bandwagon, what has led me to here?

America’s Technology Highway

I graduated from the University of Lowell (now UMass Lowell) in 1990. Overall, my college education took me 6 years to complete. It’s safe to say that I needed some motivation to finish; my two best friends there never bothered to get their degrees.

During my course of study, I worked part-time for an Engineering firm in Waltham, MA – the company which employed my father until his retirement[3] One day, my manager simply sat me down and informed me of

  1. the salary he could offer a candidate who’d earned a B.S. degree
  2. the salary he could offer a candidate who hadn’t

That was sufficient motivation. I had my degree within a year.

The Engineering firm in question did business on a grant & contract basis, so I worked on incremental projects for the DOD and other clients. Primarily I was writing hardware control systems in Visual Basic. It was a language that hardware engineers were comfortable understanding, and I’d been scripting in VB for years. Sure, I did some Objective C development for them – using a bonafide windowed Mac IDE – back in ‘91, but it was mostly all-VB all-the-time.

In 1992, my friend Tom suggested that I move to Seattle. This sounded like a good idea to me, so my employer of 7 years and I parted ways. With my belongings in tow, my friend Mark & I made the cross-country drive along I-90 in 72 hours.

Seattle

I arrived in 1992 with B.S. in Computer Science, yet apparently not a lot of relevant, desirable experience. My 70 WPM typing speed allowed me to stay afloat through temp jobs doing word processing & data entry tasks until I was hired in 1994 by a company that had in-house VBA projects. This was a significant relief, because it validated my degree and skillset, and it allowed me to expand my CV on the Microsoft platform.

Yet the job itself wasn’t nearly as influential to my career as was my introduction to the web

underground.internet.com

In the mid ’90s – you know, before the Turn of the Century – my friend Erich from ULowell was working at The Internet Company back in Boston. I found HTML authorship & publishing to be a joy, and I was given permission to host a subdomained website.

This was entirely a personal project, and not only was JavaScript an awesome tool for producing visual effects, but more importantly there was so much to be learned from the view-source of other peoples’ sites.

Let’s call the following code my Lowly Beginnings. These are excerpts from the <frameset />-based root page of that site, designed so that I could have a { position: static } header – which (a not-yet-existant) CSS would later provide with ease – yet also trigger rollover animations and related bells & whistles.

  var root = '$pathabs';
  var okBrowser = ! (((navigator.appName == "Netscape") && (parseInt(navigator.appVersion) < 3 )) || ((navigator.appName == "Microsoft Internet Explorer") && (parseInt(navigator.appVersion) < 2 )));
  var gfxBrowser = (((navigator.appName == "Netscape") && (parseInt(navigator.appVersion) >= 3 )) || ((navigator.appName == "Microsoft Internet Explorer") && (parseInt(navigator.appVersion) >= 4 )));
  var isLoaded = false;

  var cleeclikLoaded = false;

  function OnPageLoad() {
    if (! okBrowser) return;

    if ((typeof navigator.crash) != "undefined")
//    it's their own damn fault for implementing it.
      navigator.crash();

//  loaded!
    setLoaded(true);
  }

  function OnPageUnload() {
    setLoaded(false);
  }

  function setLoaded(b) {
    isLoaded = b;

    if ((typeof top.setFooterLoaded) != 'undefined')
      top.setFooterLoaded(b);
  }

  function IsHeaderLoaded() {
    return (((top.isHeaderLoaded) != 'undefined') && top.isHeaderLoaded());
  }

  function IsAlgraLoaded() {
    return (((typeof self.algra.isLoaded) != 'undefined') && self.algra.isLoaded);
  }

  function IsBoonerLoaded() {
    return (((typeof self.booner.isLoaded) != 'undefined') && self.booner.isLoaded);
  }

  function mouseover(num, inh, src) {
    if (! isLoaded) return;

    if (IsHeaderLoaded())
      parent.header.mouseover(num, 1, src);
    if (IsAlgraLoaded())
      self.algra.mouseover(num, 1, src);
    if (IsBoonerLoaded())
      self.booner.mouseover(num, 1, src);

    if (cleeclikLoaded)
      cleeclikOver(num, 1, src);
  }

  function mouseout(num, inh, src) {
    if (! isLoaded) return;

    if (IsHeaderLoaded())
      parent.header.mouseout(num, 1, src);
    if (IsAlgraLoaded())
      self.algra.mouseout(num, 1, src);
    if (IsBoonerLoaded())
      self.booner.mouseout(num, 1, src);

    if (cleeclikLoaded)
      cleeclikOut(num, 1, src);
  }

// ------------------------------------------------------

  function cleeclikLoad() {
//  loaded!
    cleeclikLoaded = true;
  }

  function cleeclikOver(num, inh, src) {
    if (! cleeclikLoaded) return;
    if ((inh == 1) && (src == 'c')) return;

    if (inh == 0) {
      mouseover(num, 1, src);

      if ((num != $sleepBot) && (num != $sleepMap))
        window.setTimeout("mouseout(" + num + ", 0, '" + src + "');", $delayTime);
    }
  }

  function cleeclikOut(num, inh, src) {
    if (! cleeclikLoaded) return;
    if ((inh == 1) && (src == 'c')) return;

    if (inh == 0)
      mouseout(num, 1, src);
  }

In real time[2], I’ve just looked at that code for the first time in probably 20 years, and said

“wow … that’s not as bad as I remember it being.” [4]

But that being said, I can’t help but notice

  • Agent-sniffing totally used to be A Thing
  • I remember liking the aesthetic of padding out comments from column 1
  • I might have been adamant about semicolons, but I was hella lazy about curly braces
  • apparently, someone believed the 0th argument to setTimeout was a String[5]
  • there was no respect from me for the sanctity of the top namespace
  • how fortunate it was that self.__NAME__ => window + <FRAME NAME="__NAME__" />[6]
  • my site was fully prepared for when a vendor might implement Navigator#crash :fist:

Yet all that aside, the technique was solid.

Cross-frame communication was incredibly reliable if the control code lived in window.top and all the frames leveraged it. Some of the ancient pages on my personal site still use this tried-and-true strategy. I’ll leave it to you, Gentle Reader, to view-source and witness my dusty code in its full glory.

In the meanwhile, I was hired by another company to maintaining their online CD-ROM catalog. I wrote a static content generator in VB, and Perl CGI scripts to render dynamic content. When that company went under, I was hired by my next employer – one of the few still in business lo these 20 years later – to work on their web-based expense management platform. It was built on IIS and used ASP to interact with in-house DDLs.

The intersection of my Visual Basic and JavaScript skills made me a valuable asset.

AJAX Before it Was a Thing

Another engineer had built a Java Applet for dynamic CRUD of expense entries. Now, this was before PaaS, so the web-based “product” was installed at and self-hosted by our corporate customers.

Well, surprise surprise, it turned out that some IT Departments were somewhat worried about the security of Java, and they didn’t want it running on in-house browsers. A project was born to replace the Java Applet with a JavaScript equivalent.

As early as 1998, I was implementing a client-server JavsScript “Applet”. The code was a freak of nature that was stable on both Netscape Navigator Gold 3.01 and IE 4.02. I can’t account for whether it was “state-of-the-art” or not, but this was my architectural solution:

  • a FrameSet providing minimal reliable cross-window interaction
  • a ‘code’ Frame where the bulk of the JavaScript logic lived
  • a ‘data’ Frame targeted by dynamically-submitted <form />s[7] which responded with payloads marshalled as DOM-parseable HTML
  • a ‘presentation’ Frame rendered using the astoundingly stable javascript: protocol
  • which is not to say there wasn’t some document.write action going in the other Frames at load-time.

Here are some redacted code snippets:

//  JSDynamic.js

    function DynamicPageWrite(wVal, tVal) {
        wVal.document.clear();
        wVal.document.write(tVal);
        wVal.document.close();
    }

    function DynamicPageLoad(wVal, tRef) {
        wVal.location = "javascript:" + tRef;
    }

    function DynamicPopupWrite(wRef, tRef, nme, ftr) {
        eval(wRef + " = window.open('javascript:\"\"', nme, ftr);");
        setTimeout("DynamicPageWrite(" + wRef + ", " + tRef + ");", 0);
    }

    function DynamicPopupLoad(wRef, tRef, nme, ftr) {
        eval(wRef + " = window.open('javascript:\"\"', nme, ftr);");
        setTimeout("DynamicPageLoad(" + wRef + ", 'opener." + tRef + "');", 0);
    }
//  JSDialogPopup.js

    var OwnerHandle = null, OwnerObject = null;
    var OwnerOpen = 1, OwnerNotify = false;
    var OwnerCanFocus = 0;

    function DialogLoaded() {
        DialogConnect();

        if ((typeof OwnerHandle.OnDialogOpen) != 'undefined') {
            OwnerObject = OwnerHandle.DialogObject;
            OwnerCanFocus = OwnerHandle.DialogCanFocus;
        }

        if (! DialogIsSilent()) {
            if (OwnerObject != null) {
                OwnerHandle.OnDialogOpen(self);
                return;
            }

            OwnerHandle.DialogOpen = 1;
        }

        if ((OwnerCanFocus != 0) && ((typeof self.focus) != 'undefined'))
            self.focus();
    }

    function DialogConnect() {
        if (OwnerHandle == null) {
            OwnerHandle = top.opener;
            OwnerNotify = true;
        }
    }

    function DialogConfirmed() {
        if (! DialogIsSilent()) {
            if (typeof(OwnerHandle.OnDialogConfirm) != 'undefined')  OwnerHandle.OnDialogConfirm();
            if (typeof(OwnerHandle.OnDialogClose) != 'undefined')  OwnerHandle.OnDialogClose();

            OwnerHandle.DialogOpen = 0;
            DialogSilent();
        }
    }

So, what have we learned?

  • tab indentation is painful :scream: this 4-space refactor only represents half the code padding
  • I hadn’t yet shaken my Hungarian Notation training
  • the comments (which I redacted out) took up about 30% of the file
  • I have a nagging memory of something about method access on a doc assigned as var doc = window.document mysteriously failing after a Document#write
  • apparently, someone still believed the 0th argument to setTimeout was a String[5]

Also, eval wasn’t truly evil until I got my hands on it. A nagging part of me is horrified at how clunky[4] it looks. But in the end, I had something very XHR-ish, and it was robust enough for Production use.

Well, at least until the memory leaks started …

San Francisco

At this point in the story, it was 1999, and the Dot-Com was booming. I’d written some Applets for my personal site, and I had decided it was time to embrace not only Java, but also a new home. So my employer & I parted ways, and I headed towards parts south.

When I tell folks how long I’ve been living in San Francisco, I use the phrase

… oh, since the Turn of the Century.

Yep, it’s the same wry joke as the title of this Post, but without the “before”.

I moved to San Francisco in May 2000 and had both an over-priced apartment and several job offers within 6 weeks. I chose the startup where I would become the first in-house Developer, though that was primarily because (refreshingly) more than half of the staff were women.

What I didn’t know at the time was how fortunate I would be when our financial backers were willing to ride out the 2001 “bubble”-pop and keep funding the company until 2005.

At one point, we were asked to implement a live Chat client. Below I’ve redacted the client-side code to negotiate with the Java Applet that proxied to our server.

var mbBrainsLoaded = false;
var mbSessionActive = false;
var miRevisionMinor = -1;

function isAppletValid() {
  var oApp;
  if ((typeof document.appChat) == "undefined")
    return false;
  oApp = document.appChat;
  if ((typeof oApp.isReady) == "undefined")
    return false;
  if (miRevisionMinor == -1) {
    if ((oApp.getState() == 'H') && (! oApp.isHistoryChanged()))
      miRevisionMinor = 4;
    else
      miRevisionMinor = oApp.getRevisionMinor();
  }
  return true;
}

function isAppletReady() {
  var oApp = document.appChat;
  return (mbBrainsLoaded && oApp.isReady());
}

function isAppletInitialized() {
  var oApp = document.appChat;
  return (isAppletReady() && oApp.isInitialized());
}

//  - - - - -

function doAppletInitialize() {
  if (! isAppletValid()) {
    top.doLoadTechnicalFrames();
    return;
  }

  var oApp = document.appChat;

  iTimeoutMinutes = 5;
  var oDate = new Date();
  var iTimeout = oDate.getTime() + (iTimeoutMinutes * 60000);
  if ((! top.isReady()) || (! isAppletReady())) {
    var oNow = new Date();
    var iNow = oNow.getTime();
    if (iNow > iTimeout) {
      top.doLoadUnavailableFrames('applet init timeout');
      return;
    } else {
      self.setTimeout('doAppletInitialize();', 100);
      return;
    }
  }

  oApp.initLocalInfo(top.getCustomerName(), top.getSHSHTYP(), top.getSHRFNBR());

  if (!isAppletInitialized()) {
    // ...
    oApp.setInitialized(true);
  }

  if (!oApp.sessionStart(top.getUniqueID())) {
    top.doLoadUnavailableFrames('start session');
    return;
  }

  var iTimeoutMinutes = Number(oApp.GetTimeOutLength());
  if (String(iTimeoutMinutes) == "NaN") iTimeoutMinutes = 5;
  var oDate = new Date();
  var iTimeout = oDate.getTime() + (iTimeoutMinutes * 60000);
  WaitForSessionAccept(iTimeout);
}

function WaitForSessionAccept(iTimeQuit) {
  var oApp = document.appChat;
  var sState = String(oApp.getState());

  mbSessionActive = false;
  switch(sState) {
    case "A":
    case "H":
      if (! ((sState == 'H') && (! oApp.isHistoryChanged()))) {
        top.doLoadReadyFrames();
        top.doHandOffInitialMessages();
        doRenderPlaybackWhenChanged();
        return;
      }
      break;
    case "U":
    case "D":
      oApp.sessionEnd();
      top.doLoadUnavailableFrames('chat denied / shutdown');
      return;
      break;
  }

  var oNow = new Date();
  var iNow = oNow.getTime();
  if (iNow > iTimeQuit) {
    oApp.sessionEnd();
    top.doLoadUnavailableFrames('session wait');
    return;
  } else {
    self.setTimeout('WaitForSessionAccept('+iTimeQuit+');', 1000);
  }
}

//  - - - - -

function onPageLoad() {
  doAppletInitialize();
  mbBrainsLoaded = true;
  top.setReadyBrains(true);
}

function doEndSession() {
  var oApp = document.appChat;
  if (! mbSessionActive)
    return;
  if (isAppletValid() && isAppletReady())
    oApp.sessionEnd();
  mbSessionActive = false;
}

function doCloseWindow() {
  if (mbSessionActive && (! confirm('Are you sure you want to end your session?')))
    return;
  doEndSession();
  top.close();
}

Yep, that looks like my coding style alright, and there are some signs of change

  • the lines are 2-space indented :thumbsup:
  • our comment indent style matched the code lines :thumbsup:
  • single quote? double quote? who cares! it JUST WORKS !!!1!
  • apparently, someone still held a strange misconception about setTimeout[5]

Our general site code had the usual event-driven visual effects and validation and other <form /> submission treatments, but this Java Applet timing handshake across <frameset />s made for some trickier stuff.

Which is all fine and dandy – but bear with me here, please – because it’s time for another diversion …

Across the Table from the Hot Seat

As Engineering Hire #1, I was involved in all of the hiring decisions of our Frontend team. I was a rather intense technical interviewer, especially when it came to the JavaScript language.

Co-workers whom we hired – who are now long-time friends – have recounted to me with amusement about our first interactions over a whiteboard. I am ashamed to say that in 2002 I brought a candidate to tears with my overwhelming line of questioning, but since that incident I’ve become much kinder and more flexible when vetting a person’s skills.

In the fullness of time, I’ve also learned that I sometimes had the wrong answers. I used to ask a candidate what you’d pass as the first argument to setTimeout, which I understood to be a String to be eval‘d[5]. Yes, that’s an answer – I mean, Chrome 57 still supports it! – but of course “a Function” is the conventional answer.

I can forgive myself in that I never told someone that “a Function” was wrong; the answer I almost always heard was “I don’t know”. But yes, I was responsible for spreading some poor advice. Thank Goddess I never spoke with someone as skilled as this guy because I would have gotten served.

Post-Reflection

In 2005, it was obvious that we weren’t going to crack the consumer market, so the company was shuttered. I then fell into a clinical depression that lasted 2-1/2 years. I survived this difficult period through no lack of supportive friends, meditative yoga and the wonders of modern chemistry.

And until I’d re-entered the post-“bubble” hiring market – which had started to recover from the crash – I did not realize how much state-of-the-art had passed me by.

Though I stayed gainfully employed during this transition time, I have some shameful memories:

  • I was early-exited from an interview (where I was out of my depth) by being told the company needed to to respond to a “technical fire”
  • A company for whom I did contract Java work declined to hire me for a J2EE position due in no small part to my mis-understanding of Transactions
  • I got let go from a company after my initial 2 weeks because my underlying depression transformed my new-hire overwhelmedness into visible stress and panic

Plus another interview SNAFU involving security doors which I find hard to summarize. Yeah – those four; those were rough ones.

It was a difficult period of time, and I grew stronger, and I survived it. In the process, I learned many lessons about the habits & discipline it takes to maintain a career in such a rapidly-moving fast-fashion industry.

I would not have the knowledge & skillset I have today[2] were it not for

Time has granted me a greater refinement of craftmanship and maturity of perspective … as will be evidenced by a significant reduction of hand-wringing going forward in this Post :+1: .

Okay, getting back on course now … Weren’t we were talking about JavaScript?

Startup Hopping, Like You Do

At the time of this writing[2], I’m on my 7th startup here in the Bay Area. Two of them have been in the gaming industry. One job was a 90-minute daily commute over to the East Bay. I have stuck around two startups to turn off the lights, and gotten out just before the collapse of two others.

This seems to be the cycle of life here in Silicon Valley; only the frameworks change. And upon each them, we implement the usual patterns.

Here I am, handling queued events in a Widget built using Prototype and the Dojo Toolkit

dojo.provide('Redacted.AbstractDialog');
dojo.widget.registerWidgetPackage('Redacted');
// ...
dojo.require('dojo.uri');
dojo.require('dojo.widget');

if (! Redacted)  { Redacted = {}; }

/**
 * abstract Dialog widget
 */
dojo.widget.defineWidget(
  'Redacted.AbstractDialog',
  'html',
  [ dojo.widget.html.Dialog ],

  // constructor
  function() { },

  // body
  {
    contextBase:  Redacted.Settings.context,
    templateBase: dojo.uri.dojoUri('../redacted/templates/'),
    // ...
    _onCloseVoters: [],
    _enablers: {},
    // ... see postCreate
    isSelfClosing: true,

    postCreate: function(args, frag) {
      // ...
      if ('isSelfClosing' in args)  { this.setIsSelfClosing(args.isSelfClosing); }

      // ...
      this._enablers = {
        close: new Redacted.Enable([ this.linkImageClose, this.linkImageCloseDisabled ], enShow)
      };
    },

    // ...
    // handlers

    _clickClose: function(evt) {
      if (! this._enablers.close.isEnabled())  { return; }

      var i, a = this._onCloseVoters;
      for (i=0; i<a.length; ++i) {
        var v = a[i], vote = true;
        if (! v.func) {
          if (dojo.lang.isFunction(v.ctx))  { vote = v.ctx(evt, this); }
        }
        else if (dojo.lang.isFunction(v.func)) {
          vote = v.func.call(ctx, evt, this);
        }
        else if (dojo.lang.isString(v.func)) {
          vote = v.ctx[v.func].call(ctx, evt, this);
        }
        if (! vote)  { return; }
      }
      if (this.isSelfClosing)  { this.hide(); }
      this.onClose(evt);
    },
  }
});

Here I am, several years later, batching AJAX requests through jquery

var Container = function(data) {
    this.batch = { queue: [], timeout: null };
    // ...

    Object.extend(this, data);
    // ...

    return this;
};

Object.extend(Container.prototype, {
    batch: null,

    // ...

    batch_send: function() {
        var b = this.batch;
        if (b.timeout) {
            window.clearTimeout(b.timeout);
            b.timeout = null;
        }
        if (b.queue.length > 0) {
            new Ajax.Request('/container/batch', {
                parameters: { queue: Object.toJSON(b.queue), authenticity_token: this.authenticity_token },
                asynchronous: true,
                evalScripts: true
            });

            b.timeout = window.setTimeout(this.batch_send.bind(this), 2000);

            b.queue.length = 0;
        }
    },
    // ...
});

Here I am, several more years down the line, performing MongoDB atomic operations which rollback unless they converge

'use strict';

var Promise = require('bluebird');

var core = require('redacted-core');
var BaseService = core.BaseService;

/**
 * @class
 * @name redacted.engage.EngageService
 */
var EngageService = BaseService.extend(
{
    // ...

    /**
     * @protected
     * @method
     * @param {Object} state
     * @param {Array<Integer>} playerIds
     * @return {Promise} a Promise resolving
     *   an Array with {@link redacted.engage.Engagements} for each of the Players
     */
    _engagePlayers: function(state, playerIds) {
        var self = this;
        var Ctor = this.constructor;
        var Engagements = this.model(Ctor.modelName);
        var engagementId = state.id;
        var mightRollback = [];

        return Engagements.find(
            { _id: { $in: playerIds } },
            null,
            { sort: { _id: 1 } } // in repeatable ID order
        ).exec()
        .then(function(engagements) {
            return Promise.all(engagements.map(function(engagement) {
                var playerId = engagement._id;

                try {
                    _assertPlayerNotEngaged(playerId, engagement);
                }
                catch (e) {
                    return self._scheduleFixupPlayer(playerId)
                    .throw(new Error("Player " + playerId + " is otherwise engaged"));
                }
            }))
            .return(engagements); // propagate, vs. scoped var
        })
        .then(function(engagements) {
            return engagements.reduce(function(chain, engagement) {
                var playerId = engagement._id;

                return chain
                .then(function() {
                    return Engagements.findAndModify(
                        { _id: playerId, engagedIn: Const.NO_CURRENT }, // assuming you're not previously engaged
                        { },
                        { $set: { engagedIn: engagementId } }
                    );
                })
                .then(function(result) {
                    if (! result) {
                        throw new Error("Player " + playerId + " is otherwise engaged");
                    }
                    mightRollback.push(engagement);
                });
            }, Promise.resolve());
        })
        .then(function() {
            if (mightRollback.length !== playerIds.length) {
                throw new Error("at least one Player is otherwise engaged");
            }

            // $findAndModify is done out-of-band with the in-memory Model cache
            mightRollback.forEach(function(engagement) {
                engagement.__attributes.engagedIn = engagementId;
            });

            return mightRollback;
        })
        .catch(function(err) {
            return self._rollbackEngagement(state, mightRollback)
            .catch(_absorbAnyError)
            .throw(err);
        });
    },
});

module.exports = PvpService;

Alright, alright … that’s some lovely ancient history. I commend both your patience and your spirit, dear Reader.

Because it all leads us to …

Today

Honestly, today[2] most of my ‘living’ projects are Ruby scripts. Do I need to write me some expressive synchronous code which gets executed from the command line and has no performance requirements? Why yes, yes I do. Ruby is excellent for that, and in these cases I choose it over JavaScript.

I am gainfully employed writing JavaScript 90% of the time (curse you CoffeeScript). And I maintain a bunch of JavaScript-related projects in my private repo. But what’s more interesting is my public “showcase” repo.

It’s the JavaScript ES6[8] app which I built to replace all the legacy Perl CGI scripts for sleepbot.com, the long-lived descendant of my first website. Feature-wise, it’s complete fluff, because the Perl scripts it replaced were super-simple. The interesting part is in the project’s README … which has some silly Badges … you know, for build status and up-to-date-ness, and …

Crap. See, in real time, I just found out that my Travis CI build is broken. In the last commit, I upgraded to request@2.81.0, which works great in Node 6, but is unsupported in Node 0.10 and blew up on latest (Node 7.10 as of when I pushed). I added tests for all those versions just because I was curious.

So now, because I haven’t fixed it – which I haven’t[2] – my “showcase” project is out there on Github declaring itself broken to the world. My choices are to (a) have a broken build; (b) have a David DM Badge shaming me for being out-of-date, or; (c) only build against Node 6

I mean, all the cool kids use Repository Badges. I wonder how they manage the social pressure of maintaining them :relieved:

The cool kids also commit PRs to fix issues in Open Source projects. I’ve done that too, and mostly for JavaScript packages.

Several times a year I offer to mentor for NodeSchool SF. It’s an opportunity for me to “give back” to the community while I level up on ‘soft’ skills like How to Teach and How to Lead. I was the initial contributor to their Event Mentor Best Practices Guide.

In my Day Job, I have the opportunity to play with some of the Frontend toys the cool kids use. For example, here I am writing React at a truly introductory level

import React, { PropTypes } from 'react';
import classNamesBind from 'classnames/bind';
import noop from 'lodash/noop';
import styles from './Checkbox.css';

const classNames = classNamesBind.bind(styles);

function Checkbox({ onChange, checkboxStyle, label, disabled, ...rest }) {
  const labelClassname = styles[`${checkboxStyle}Label`];
  const labelTextClassname = classNames({
    [ `${checkboxStyle}LabelText` ]: true,
    disabledLabelText: disabled,
  });

  return ( <label className={labelClassname}>
    <input
      type="checkbox"
      onChange={onChange}
      disabled={disabled}
      {...rest}
    />
    <span className={labelTextClassname}>{label}</span>
  </label> );
}

Checkbox.propTypes = {
  onChange: PropTypes.func,
  disabled: PropTypes.bool,
  checkboxStyle: PropTypes.string,
  label: PropTypes.string.isRequired,
};

Checkbox.defaultProps = {
  onChange: noop,
  checkboxStyle: 'normal',
};

export default Checkbox;

And ya know, in this moment I can’t help but think to myself

Oh gawd, that’ll look so crappy to me in 3 years time.

I would say that developing in the JavaScript language and its rich tapestry of frameworks and toolchains does not get boring.

The Future

As of today[2], if you do a view-source of my Current Lister World Map and extract the code, you’ll see that it’s still written using Prototype and DWR. The rest of my radio station’s client code still uses an ancient JavaScript code loader called JSAN.

For yeeears, I’ve been saying

You know, I really should re-write that code …

But, ya know, it just plain works. I figure; as long as the client doesn’t throw Errors, and I can repeatedly re-launch my servers into AWS, I shouldn’t spend time re-inventing those wheels. Yep, those creaky ol’ rust-flakin’ wheels.

I’ve been spending time lately[2] putting together site build chains. Inlining, SVG optimization, code bundling, all that good stuff. At the top, there’s a Makefile, and it drives the gulp tooling. Before gulp, there was grunt, and also Yeoman. Now there’s webpack and rollup and parcel. Oh, oh, … and remember GWT :sweat_smile: ?

I’m proud to say that my radio station stack – Java backend and all — has been running smoothly for twelve years now. I’ve always tried to make technical choices that have long-term maintenance potential. Choices like using Makefiles. By and large, those principles have served me well.

I wonder about my new from-scratch JavaScript projects. Am I investing in what may become some very ephemeral tooling? Will gulp continue to work “forever”, the way my ol’ Ant tasks do? Gosh, let’s find out!

Some day, I might actually re-write that code! Maybe in React and Redux. Or Vue. Definitely not in Angular, because Angular 2. And I don’t figure Ember or Backbone. Nope, no Prototype or Dojo or YUI or jQuery UI or … wait, there were others too … umm.   Well, yeah. You get my point.

As I get older in tech, the more I’ve come to appreciate my repeatable tooling, my documentation and those times that I pay-it-forward to myself with Test Suites. And above all, how nice it is that, to this day, it just plain works.

In conclusion, reproducible processes are critical to — oh, look! :sparkles: sparkly :sparkles:

Node 8.3.0 runs on top of V8 6.0 now. Mmm hmm, and a new JIT means better performance. In the long run, maybe Google will implement ‘strong’ mode. Because performance is cool.

I’ve loved me some async / await that I’ve written. And ES6[8] object destructuring is faaabulous !!!

:rocket: To The Future!


[1]

“though not all” <= See what I mean! I didn’t have to say that. Sheesh.

[2]

Where “today” is circa Summer 2017, the dawn of my 50th year.

This too will be lost, in time, like tears in rain.

[3]

I cannot understate the privilege of having been guided into a professional Engineering environment by my father, a fine Electrical Engineer in his own right.

[4]

When I started writing this Post, I worked hard to to accept the fact that I was dredging up ancient stuff, and that it wasn’t going to be pretty. I rationalized:

no Developer is ever that happy with their old stinky code

I bring this archaicness into the light of day in a spirit of full disclosure :relieved: which documents my stylistic evolution over the decades.

[5]

Yep … from my first website to my proto-AJAX implementation and on into the early 2000s and my interview questions, I sure believed some crazy shit.

[6]

For the longest time, I wrote all of my HTML Element markup in all-caps. Dammit, why am I admitting to these things ??

[7]

The page-specific <form />-build-and-submit() code was rendered as <script />s by ASP server-side logic. It’s 1999-era cool, but kinda scary to visually parse nearly 20 years later. I’ll spare you the horror of having to see it yourself.

[8]

Wait, was it ES6 or ES2015? I get the impression that it’s complicated.