MacBook, defective by design banner

title:
Put the knife down and take a green herb, dude.


descrip:

One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com

Using 89% of the same design the blog had in 2001.

FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.

x

MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Friday, October 14, 2016

Let's talk HTML Fragments in your clipboard when coding on Windows. I've created what's arguably the best Markdown editor on Win10 (if I do say so myself), and wanted it to be more intelligent when I was pasting content that comes from web browsers. That is, in the original version of MarkUpDown, if you copied the text I've got highlighted, below, from DaringFireball...

Quote from Walt Mossberg on Siri plus a little bit from Gruber, now with visible context menu saying "Copy"

... and you pasted it into MarkUpDown as a quotation (Ctrl-Shift-V), you'd get unstyled text, like this...

> Mossberg:
> For instance, when I asked Siri on my Mac how long it would take me to get to work, it said it didnโ€™t have my work address โ€” even though the โ€œmeโ€ contact card contains a work address and the same synced contact card on my iPhone allowed Siri to give me an answer.
> Similarly, on my iPad, when I asked what my next appointment was, it said โ€œSorry, Walt, somethingโ€™s wrongโ€ โ€” repeatedly, with slightly different wording, in multiple places on multiple days. But, using the same Apple calendar and data, Siri answered correctly on the iPhone.
> These sort of glaring inconsistencies are almost as bad as universal failures. The big problem Apple faces with Siri is that when people encounter these problems, they stop trying.

I mean, that's okay-ish. Unfortunately, you'd have to go put in an additional line after each line so that it's not all scrunched into one paragraph. And you lose that Gruber was block-quoting Mossberg. And you lose that "they stop trying" was originally in italics. That stinks.

Isn't Markdown really supposed to be shorthand for html? Can't we handle pasting html better than that?

Yes, yes we can...


TL;DR

Go pull the HtmlFragmentHelper, a working, in-progress library for turning HTML Fragment strings into view models with these properties...

public class HtmlFragmentViewModel
{
    public string Version = "";
    public int StartHtml = int.MinValue;
    public int EndHtml = int.MinValue;
    public int StartFragment = int.MinValue;
    public int EndFragment = int.MinValue;
    public string SourceUrl = "";
    public string FragmentSourceRaw = "";
    public string Error = "";

    //...
}

There are also several convenience methods to help you get, say, the top- and second-level domains for the HTML Fragment's source URL (example.com from http://blog.example.com/user/blog0000231.html), or just the html from the selected fragment (myHtmlFragmentViewModel.FragmentSourceParsed), etc.

Here's an example:

public string CreateQuote(string htmlFragmentSource) 
{
    string strIntroLink = string.Empty;
    HtmlFragmentViewModel vm = new HtmlFragmentViewModel(htmlFragmentSource);

    if (vm.SourceUrl.Length > 0 && vm.SourceUrlDomainSecondAndTopLevelsOnly.Length > 0)
    {
        strIntroLink = string.Format("From <a href=\"{0}\">{1}</a>:" + Environment.NewLine + Environment.NewLine,
            vm.SourceUrl, vm.SourceUrlDomainSecondAndTopLevelsOnly);
    }

    return strIntroLink + vm.FragmentSourceParsed;
}

Read how to use it here. Get it from GitHub, though the only file you really need is here.

And here's a quick article on how to get code back into the clipboard as html, which I'm not doing.


HTML Fragment Format

Want to paste clipboard HTML as HTML? Enter Microsoft's HTML clipboard format. Instead of using the standard code to read the clipboard's contents, like this...

string clipboardText = await Clipboard.GetContent().GetTextAsync();

... now you use this...

string htmlClipboardText = await Clipboard.GetContent().GetHtmlFormatAsync();

But when you look at the contents of htmlClipboardText, you quickly notice that strange things are afoot at the Circle K.

What number are you thinking of?

What comes back isn't a string with html source, but an "HTML Fragment" formatted string that looks like this:

HTML Fragment from Edge

Version:1.0
StartHTML:000000210
EndHTML:000003550
StartFragment:000002696
EndFragment:000003500
StartSelection:000002696
EndSelection:000003496
SourceURL:http://daringfireball.net/2016/10/mossberg_siri
<!DOCTYPE HTML>
<HTML lang="en"><HEAD>       <!-- Open Graph [jive] --> <!-- 
    <meta property="og:site_name"   content="Daring Fireball" />
    <meta property="og:title"       content="Walt Mossberg: โ€˜Why Does Siri Seem So Dumb?โ€™" />
    <meta property="og:url"         content="http://daringfireball.net/2016/10/mossberg_siri" />
    <meta property="og:description" content="In addition to the engineering hurdles to actually make Siri much better, Apple also has to overcome a โ€œboy who cried wolfโ€ credibility problem." />
    <meta property="og:image"       content="https://daringfireball.net/graphics/df-square-192" />
    <meta property="og:type"        content="article" />
 -->         <!-- Twitter Card [jive] -->                    <TITLE>Daring Fireball: Walt Mossberg: โ€˜Why Does Siri Seem So Dumb?โ€™</TITLE>        <LINK href="/graphics/apple-touch-icon.png" rel="apple-touch-icon-precomposed">     <LINK href="/graphics/favicon.ico?v=005" rel="shortcut icon">   <LINK href="/graphics/dfstar.svg" rel="mask-icon" color="#4a525a">  <LINK href="/css/fireball_screen.css?v1.7" rel="stylesheet" type="text/css" media="screen">     <LINK href="/css/ie_sucks.php" rel="stylesheet" type="text/css" media="screen">     <LINK href="/css/fireball_print.css?v01" rel="stylesheet" type="text/css" media="print">    <LINK href="/feeds/main" rel="alternate" type="application/atom+xml">   
<SCRIPT src="/mint/?js" type="text/javascript" async=""></SCRIPT>

<SCRIPT src="http://www.google-analytics.com/ga.js" type="text/javascript" async=""></SCRIPT>

<SCRIPT src="/js/js-global/FancyZoom.js" type="text/javascript"></SCRIPT>

<SCRIPT src="/js/js-global/FancyZoomHTML.js" type="text/javascript"></SCRIPT>
     <LINK title="Home" href="/" rel="home">     <LINK href="http://df4.us/pfz" rel="shorturl">  <LINK title="Apple Responds to Dash Controversy" href="http://daringfireball.net/2016/10/apple_dash_controversy" rel="prev">    
<SCRIPT src="http://daringfireball.net/mint/?record&amp;key=383950464d37374b39333637695970466458724e6779513431&amp;referer=&amp;resource=http%3A//daringfireball.net/2016/10/mossberg_siri&amp;resource_title=Daring%20Fireball%3A%20Walt%20Mossberg%3A%20%u2018Why%20Does%20Siri%20Seem%20So%20Dumb%3F%u2019&amp;resource_title_encoded=0&amp;window_width=1756&amp;window_height=921&amp;resolution=2438x1371&amp;flash_version=0&amp;1476397798179&amp;serve_js" type="text/javascript"></SCRIPT>
</HEAD><BODY onload="setupZoom()"><DIV id="Box"><DIV id="Main"><DIV class="article"><!--StartFragment--><P>Mossberg:</P><BLOCKQUOTE><P>For instance, when I asked Siri on my Mac how long it would take me to get to work, it said it didnโ€™t have my work address โ€” even though the โ€œmeโ€ contact card contains a work address and the same synced contact card on my iPhone allowed Siri to give me an answer.</P><P>Similarly, on my iPad, when I asked what my next appointment was, it said โ€œSorry, Walt, somethingโ€™s wrongโ€ โ€” repeatedly, with slightly different wording, in multiple places on multiple days. But, using the same Apple calendar and data, Siri answered correctly on the iPhone.</P></BLOCKQUOTE><P>These sort of glaring inconsistencies are almost as bad as universal failures. The big problem Apple faces with Siri is that when people encounter these problems, <EM>they stop trying</EM>.</P><!--EndFragment--></DIV></DIV></DIV></BODY></HTML>

Wow. No, really, wow. That surprised me. I was using Edge this time instead of Chrome, and whoa. Edge includes the entire page's header. That said, that's really not a horrible idea. We have really good context for this fragment, and if we wanted the original CSS, for example, we know where to get it.

Aside: Look at the info DaringFireball's sucking in:

resolution=2560x1440&amp;
flash_version=0&amp;
1476452395012&amp;
serve_js

Gruber doesn't use Flash. Kinda Panopticlicky, ain't it? Looks like it's supporting this.

Another Aside: Internet Explorer 11 creates the same fragment source, so if you think Edge was created from the ground up...

Interestingly, the old Internet Explorer copyright box used to reference Mosaic, but that's gone in IE11. Wonder if IE11 is really the child of IE6 and 7? But enough aside-ing...

HTML Fragment from Chrome

But let's take a look at where I initially started, with Chrome.

Version:0.9
StartHTML:0000000164
EndHTML:0000002719
StartFragment:0000000200
EndFragment:0000002683
SourceURL:http://daringfireball.net/2016/10/mossberg_siri
<html>
<body>
<!--StartFragment--><p style="margin: 0px 0px 1.6em; padding: 0px; color: rgb(238, 238, 238); font-family: Verdana, &quot;Bitstream Vera Sans&quot;, sans-serif; font-size: 11px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(74, 82, 90);">Mossberg:</p><blockquote style="font-size: 11px; margin: 2em 2em 2em 1em; padding: 0px 0.75em 0px 1.25em; border-left: 1px solid rgb(119, 119, 119); border-right: 0px solid rgb(119, 119, 119); outline: 0px; vertical-align: baseline; background: rgb(74, 82, 90); color: rgb(238, 238, 238); font-family: Verdana, &quot;Bitstream Vera Sans&quot;, sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-t
ransform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><p style="margin: 0px 0px 1.6em; padding: 0px;">For instance, when I asked Siri on my Mac how long it would take me to get to work, it said it didnโ€™t have my work address โ€” even though the โ€œmeโ€ contact card contains a work address and the same synced contact card on my iPhone allowed Siri to give me an answer.</p><p style="margin: 0px 0px 1.6em; padding: 0px;">Similarly, on my iPad, when I asked what my next appointment was, it said โ€œSorry, Walt, somethingโ€™s wrongโ€ โ€” repeatedly, with slightly different wording, in multiple places on multiple days. But, using the same Apple calendar and data, Siri answered correctly on the iPhone.</p></blockquote><p style="margin: 0px 0px 1.6em; padding: 0px; color: rgb(238, 238, 238); font-family: Verdana, &quot;Bitstream Vera Sans&quot;, sans-serif; font-size: 11px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacin
g: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(74, 82, 90);">These sort of glaring inconsistencies are almost as bad as universal failures. The big problem Apple faces with Siri is that when people encounter these problems,<span class="Apple-converted-space">ย </span><em>they stop trying</em>.</p><!--EndFragment-->
</body>
</html>

See the difference? Chrome's fragment initially looks much more focused, but also provides much less information about the code's original context. Worse, Chrome's puts all the CSS information inline, and repeating each CSS property inside of each pasted html element makes the source a long ways away from DRY. And that out-of-context, inline CSS is lossily translated, which will cause us some rendering problems that we'll see and discuss in a bit.

Original Source

Now Edge's fragment isn't perfect. Here's the original html of that snippet straight from the server...

<p>Mossberg:</p>

<blockquote>
  <p>For instance, when I asked Siri on my Mac how long it would take
me to get to work, it said it didnโ€™t have my work address &#8212;
even though the โ€œmeโ€ contact card contains a work address and
the same synced contact card on my iPhone allowed Siri to give
me an answer.</p>

<p>Similarly, on my iPad, when I asked what my next appointment was,
it said โ€œSorry, Walt, somethingโ€™s wrongโ€ &#8212; repeatedly, with
slightly different wording, in multiple places on multiple days.
But, using the same Apple calendar and data, Siri answered
correctly on the iPhone.</p>
</blockquote>

<p>These sort of glaring inconsistencies are almost as bad as universal failures. The big problem Apple faces with Siri is that when people encounter these problems, <em>they stop trying</em>.

NOTE: There's no closing </p> tag because I've cut the source at that spot.

Look how clean the original is! Why can't we just get that? Chrome's CSS injections and line flattening makes for a much less human readable snippet.

Edge is close, but still weird. Take another look. For some reason, Edge's fragment makes every tag uppercase like it's 1998. It also trashes Gruber's original (and thoughtful) whitespace. Also yuck. I mean, Gruber's using Markdown to write Daring Fireball. In a perfect world, I'd take that clipboard's html and turn it back into Markdown when it's pasted into MarkUpDown.

How do we get this original source?

Start and End Fragment

The important thing to catch is that the HTML Fragment format almost always includes a full html doc with html and body tags after the SourceURL and other metadata. Within that html code, there are markers, <!--StartFragment--> and <!--EndFragment-->, saying where the exact selection started and stopped.

Though Chrome immediately dives into your selected text (the "true" html fragment) immediately after the <body> tag, Edge gives you any open tags that come before your fragment too. In this case, you had three open div tags when you got to the highlighted selection: <DIV id="Box"><DIV id="Main"><DIV class="article">. Knowing this DOM could be really helpful if you were interested in retrieving the original CSS and formatting your fragment as close to the original markup as possible.

If you ignore context like this, though, it's an easy enough feat to take the string and parse out the "true fragment's" html by splitting the entire HTML Fragment string on <!--StartFragment--> and <!--EndFragment-->. Here's an early version of something I wrote to just take out the html, ignoring all the fragment metadata.

private string _parseHtmlClipboardFragment(string rawFragmentSource)
{
    string ret = rawFragmentSource;
    string delimiterStartAfter = "<!--StartFragment-->";
    string delimiterEndBefore = "<!--EndFragment-->";

    if (-1 < ret.IndexOf(delimiterStartAfter))
    {
        ret = ret.Substring(ret.IndexOf(delimiterStartAfter) + delimiterStartAfter.Length);
        if (-1 < ret.IndexOf(delimiterEndBefore))
        {
            ret = ret.Substring(0, ret.IndexOf(delimiterEndBefore));
        }
        else
        {
            ret = string.Empty;  // No luck, Ending not after Start; go back to nothing.
        }
    }

    return ret;
}

NOTE: I've got a nice library that parses all this up into a custom html fragment view model that I mentioned in TL;DR. I wouldn't and don't use the above code in most cases.

When we parse out that html and insert it into our Markdown from Chrome, however, we get some, um, interesting results.

NOTE: Remember that Markdown is a superset, so to speak, of html. You could have a "Markdown" file without any Markdown syntax that's pure html. So what I'm really saying is, "Let's see what those html snippets that we pulled from the fragment look like when they're injected into our regularly scheduled html."

I'm going to wrap these pastes in blockquote tags so that you can tell where they start and stop easily.

Edge Parsed Code

Mossberg:

For instance, when I asked Siri on my Mac how long it would take me to get to work, it said it didnโ€™t have my work address โ€” even though the โ€œmeโ€ contact card contains a work address and the same synced contact card on my iPhone allowed Siri to give me an answer.

Similarly, on my iPad, when I asked what my next appointment was, it said โ€œSorry, Walt, somethingโ€™s wrongโ€ โ€” repeatedly, with slightly different wording, in multiple places on multiple days. But, using the same Apple calendar and data, Siri answered correctly on the iPhone.

These sort of glaring inconsistencies are almost as bad as universal failures. The big problem Apple faces with Siri is that when people encounter these problems, they stop trying.

Though you should remember that the original source has been smeared together, losing its newlines, and all the tags are strangely capitalized, the source looks pretty nice. It's plain, and adopts the local styling for the most part, but the most important markup is still there -- the blockquote, the italics, links if they'd been included, etc.

Chrome Parsed Code

Mossberg:

For instance, when I asked Siri on my Mac how long it would take me to get to work, it said it didnโ€™t have my work address โ€” even though the โ€œmeโ€ contact card contains a work address and the same synced contact card on my iPhone allowed Siri to give me an answer.

Similarly, on my iPad, when I asked what my next appointment was, it said โ€œSorry, Walt, somethingโ€™s wrongโ€ โ€” repeatedly, with slightly different wording, in multiple places on multiple days. But, using the same Apple calendar and data, Siri answered correctly on the iPhone.

These sort of glaring inconsistencies are almost as bad as universal failures. The big problem Apple faces with Siri is that when people encounter these problems,ย they stop trying.

Um, ew. Wha' happen'd?

Unfortunately, even though Daring Fireball's entire page has the same styles applied, this inline kludge Chrome performs puts it only in each, in this case, <p> tag. So when we have a margins around paragraphs, there's nothing telling us to also put that color and style between the block-level tags. There's no surrounding tag to be a stand-in for, essentially, the body (or any other lost, open) tag.

Let's look at all the overhead we have for each p from that fragment:

<p style="
    margin: 0px 0px 1.6em;
    padding: 0px;
    color: rgb(238, 238, 238);
    font-family: Verdana, &quot;Bitstream Vera Sans&quot;, sans-serif;
    font-size: 11px;
    font-style: normal;
    font-variant-ligatures: normal;
    font-variant-caps: normal;
    font-weight: normal;
    letter-spacing: normal;
    orphans: 2;
    text-align: left;
    text-indent: 0px;
    text-transform: none;
    white-space: normal;
    widows: 2;
    word-spacing: 0px;
    -webkit-text-stroke-width: 0px;
    background-color: rgb(74, 82, 90);
">

And here's what we have from Chrome's Dev Tools' "Computed" style tab:

color: rgb(238, 238, 238);
display: block;
font-family: Verdana, "Bitstream Vera Sans", sans-serif;
font-size: 11px;
height: 19px;
line-height: 19.8px;
margin-bottom: 17.6px;
margin-left: 0px;
margin-right: 0px;
margin-top: 0px;
padding-bottom: 0px;
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
text-align: left;
text-size-adjust: 100%;
width: 425px;
-webkit-margin-after: 17.6px;
-webkit-margin-before: 0px;
-webkit-margin-end: 0px;
-webkit-margin-start: 0px;

But note that that's essentially all from one css file, as properties from user agent stylesheets are simply the browser's defaults:

Paragraph styles for Daring Fireball

If you'd like, we can compare the fragment's CSS vs. the stylesheet. Spoiler: It's a mess. Not a lot of matching. Click here to see. Ugly.

The bottom line is that background-color: rgb(74, 82, 90); is something that belongs to the body, but is inserted here by Chrome into its HTML Clipboard contents as something that's attached to each block element. That's wrong. It doesn't display correctly, and it needlessly clutters the resulting HTML.

With HtmlFragmentHelper, I have a couple of choices. One is to strip all inline styles out, or at least some subset of styling. If I want to maintain as much of the original as possible, I might just blast anything that injects colors into the html, like color and background-color. Or I could blast all the inline style info and get something more like Edge's code.

Or I could normalize the CSS and optionally (via a property on HtmlFragmentViewModel) wrap it all in a block-level tag that contains CSS that's everywhere. Or at least in all of the top-level tags. But now I'm starting to write an html parser, which is a little outside of my current scope.

I think I'm going to do the former, though it's not in there as of this writing. That Chrome paste looks horrible as is, though. If only there was an easier way...


HTML Fragment from Firefox

There are certainly other applications that create HTML Clipboard values. I ran into LibreOffice's when using Calc, their spreadsheet app, and its html clipboard doesn't include Start and EndFragment tags. Wonderful, folks, wonderful.

But isn't there one glaring omission I might want to add here? You know, I think there is at least one...

For fun, let's take a look at Firefox's fragment too. Firefox isn't as popular as it used to be, but it's probably worth taking a look. (Remember that this is all on Windows, and Safari is dead enough by now that I'm not going to bother with v5.)

Version:0.9
StartHTML:00000156
EndHTML:00001060
StartFragment:00000190
EndFragment:00001024
SourceURL:http://daringfireball.net/2016/10/mossberg_siri
<html><body>
<!--StartFragment--><p>Mossberg:</p>

<blockquote>
  <p>For instance, when I asked Siri on my Mac how long it would take
me to get to work, it said it didnโ€™t have my work address โ€”
even though the โ€œmeโ€ contact card contains a work address and
the same synced contact card on my iPhone allowed Siri to give
me an answer.</p>

<p>Similarly, on my iPad, when I asked what my next appointment was,
it said โ€œSorry, Walt, somethingโ€™s wrongโ€ โ€” repeatedly, with
slightly different wording, in multiple places on multiple days.
But, using the same Apple calendar and data, Siri answered
correctly on the iPhone.</p>
</blockquote>

<p>These sort of glaring inconsistencies are almost as bad as universal 
failures. The big problem Apple faces with Siri is that when people 
encounter these problems, <em>they stop trying</em>.</p><!--EndFragment-->
</body>
</html>

Oh, so beautiful. Same whitespace as the original, with the added bonus (?) that the final paragraph is also text wrapped. No crazy inline CSS attempted. No header overhead or 1998-style HTML TAGS.

You could argue that Chrome is better because it has that inline CSS (yuck!) or Edge because it has the full page headers, but honestly, we're only one step farther away from what Edge gave us. We have the URL here. If we have network access to get CSS, we have network access to read the header and figure out where the CSS lives ourselves. Don't overcomplicate things. Beautiful. Good looking code continues to look good when pasted inline with Markdown.

Firefox Parsed Code

And Firefox's snippet is just as nice when pasted.

Mossberg:

For instance, when I asked Siri on my Mac how long it would take me to get to work, it said it didnโ€™t have my work address โ€” even though the โ€œmeโ€ contact card contains a work address and the same synced contact card on my iPhone allowed Siri to give me an answer.

Similarly, on my iPad, when I asked what my next appointment was, it said โ€œSorry, Walt, somethingโ€™s wrongโ€ โ€” repeatedly, with slightly different wording, in multiple places on multiple days. But, using the same Apple calendar and data, Siri answered correctly on the iPhone.

These sort of glaring inconsistencies are almost as bad as universal failures. The big problem Apple faces with Siri is that when people encounter these problems, they stop trying.

I miss Firefox.

Labels: , , , , , , ,


posted by ruffin at 10/14/2016 11:46:00 AM
Saturday, September 26, 2015

Edit: Turns out something smellier than I expected is going on with the NYT and ebooks. From the Stratechery piece:

To my regret, and in a rich bit of irony, I failed to research disconfirming evidence for the New York Timesโ€™ conclusion that ebook sales were indeed dropping.

Fortunately, the Author Earnings blog took no such shortcuts and came to some different conclusions. I strongly suggest reading the whole thing, but here are some pertinent excerpts...

And now, back to what I originally wrote...


Interesting but overly simple NYT article from @Gruber on "The Plot Twist: E-Book Sales Slip, and Print Is Far From Dead". Read the article (I'm not going to summarize past the title), and let me try to paint a slightly different picture using the same content:

As readers migrated to new digital devices, e-book sales soared, up 1,260 percent between 2008 and 2010, alarming booksellers that watched consumers use their stores to find titles they would later buy online.
...
Then in 2011, the industry's fears were realized when Borders declared bankruptcy.
...
E-book subscription services, modeled on companies like Netflix and Pandora, have struggled to convert book lovers into digital binge readers, and some have shut down. Sales of dedicated e-reading devices have plunged as consumers migrated to tablets and smartphones.

Let's recombine those statements with a few others...

The American Booksellers Association counted 1,712 member stores in 2,227 locations in 2015, up from 1,410 in 1,660 locations five years ago.

Wonder what happened when Borders closed? Hrm... Let's add Ben Thompson's slightly ecologically misapplied Internet jungle metaphor, where you've got the apex predators getting bigger (here, publishing houses), the niche competitors growing in their specialized niches, and nothing in between. That is, guess where underserved book buyers buy books?

Higher e-book prices may also be driving readers back to paper.

As publishers renegotiated new terms with Amazon in the past year and demanded the ability to set their own e-book prices, many have started charging more. With little difference in price between a $13 e-book and a paperback, some consumers may be opting for the print version.

Let's also remember...

  • Tablet sales are taking.
  • Apple's ebook suit was settled, and the only significant downward pressure on price is also gone.
  • The bookshelf building stage of ebook ownership is over, in large part because of raised ebook prices. Your whales have reached critical mass, and are buying only just over the speed at which they're reading.

Here's an alternative take to the latent "print is making a comeback" argument in the article (and @Gruber):

The ebook's initial position as a bargain print substitute pushed large merchants, unable to pivot and compete on convenience and price, off of their perches.

Years later, however, growth has stabilized for both print and pixels because...

  1. Tablet sales have dropped, making tablet ownership flatten
  2. eBook prices have increased as competitive pricing in ebooks has disminished
  3. eBook readers have filled their virtual bookshelves, and are less impulsive about buying
Makes more sense to me.

And a quick thought on @Gruber's hipster comment:

I read both, but for any book I truly care about, I prefer to get it in print.

Combine that with this statement from the NYT article:

And according to some surveys, young readers who are digital natives still prefer reading on paper.

I used to think that too, and want to keep thinking it, but I don't any more.

What our young, hipster readers are saying is that they prefer the experience one gets from reading a book successfully offline. Think of all the things that have to happen for you to read a printed book...

  1. You have to have it with you.
  2. You need good lighting.
  3. You have to have time to read it.
  4. It has to be good enough for you to finish.

Below, I'm going to argue that we no longer read paperbacks in the grocery line, or while waiting for a friend, or over lunch, like we used to. If we stipulate that, one thing becomes clear:

Reading a paper book is now about having time to dedicate to reading. Space, light, comfort. So of course we'd rather have a book in print, because we recall a more pleasurable experience. It's not the book that's great so much as what reading a printed book "requires".

I used to carry around a book everywhere in the 80s and 90s. It was, looking back, my smartphone, so to speak. It was the small, portable device that best allowed you to use up dead time by sneaking in a few moments of escapist pleasure. I used to read a book in the Dune series every day or two until I caught up with Frank, just before he died. Nothing wrong with that, within reason.

But now the alternative is too handy. I have my phone with my all the time. I no longer carry a paperback. It's too easy to have something to read on the phone.

My suspicion is that most read (if they are reading, and not Clash of Clanning, which is also fine, within reason!) more web stories, Instapapered or otherwise, RSS, and email on their phone to help fill up that "catch as catch can" time. Personally, I tend to be reading at least one paper book and one ebook all the time in large part to be ready for down time. I love to have the space to read a printed book. I love to mark portions that are interesting to pull back out later. But when I'm waiting on friends or find myself stuck in a line, it's hard to grab a book beside my chair at home. It's really easy to yank out an iPod or smartphone.

Most importantly, I have both bookshelves full with books so I don't run out of things to read if I have time to relax or have unexpected time to kill. And that's largely why my ebook purchasing is flat. I have my reservoir. I'm less likely to bite on today's deal.

I might prefer have time to sit in a chair on the porch with a drink when there's great light and weather to read, but just like my camera, the best book to read is always the book I have with me.

Labels: , , , ,


posted by ruffin at 9/26/2015 10:30:00 AM
Wednesday, August 19, 2015

So Dan Counsell kicked the hornets' nest again regarding MAS upgrades.

I know there's always been abandoned software and there always will be. It just feels like there's more of it now than ever before. Why is this?

One of the main culprits is the lack of paid upgrades on the Mac App Store.

Whoa. Any time you blame Apple, take a step back[1].

Aren't there ways to provide paid upgrades? I mean, really, is it that complicated to dream up a way to include paid upgrades using In-App Purchases (IAPs)?

I understand that the MAS doesn't have a simple mechanism for making app upgrades, and is pretty clearly trying to, at least implicitly, encourage you to make software that your user buys only once and receives free upgrades forever. I understand that this isn't sustainable for indie devs, but I also know we're smart enough to work around it.

The problem's solution

So let's say I was, I don't know, writing an email client that I release just before or during 2016, and it costs you $55. It turns email from timesink to a thoughtless extension of your mind, and you can't stop yourself from clicking Buy.

During 2016, I code like mad and add a host of new features. Heck, let's get crazy and say I rewrite the entire app (btw, never do that). Now I want to release EmailClient 2017 for $55 for new folks, but only $10 for you, my loyal customer.

Seems easy on its face, doesn't it? I keep EmailClient on the MAS. It now has an IAP called, "Super Groovy 2017 features" that runs you $10. But, get this, if you never had the app installed, you get these features for free! Wait, what? Wacky, huh?

Implementation

The "only" problem is implementation, and yep, it's a problem[2]. But isn't that what we do? We solve coding problems.

How could we do it? Well, if you have an email client, you have all sorts of methods, the most straightforward probably being to save a hash to a sandboxed folder with some user-identifying info, some salt, and the date of first use. And any app could do this reasonably safely, even without a simple web service, making the unlock of new features difficult, though obviously not impossible, to crack.[3].

Poof. We have upgrade pricing. Right? I mean, it's a little code smelly, but we have it.

EDIT: I guess there's one issue -- we'll have an IAP those users don't need to buy. That's a tough call; I haven't played around with IAP on the MAS yet. Any context there? Can you hide an IAP inside of the app? Or direct folks directly to the IAP? I think the only IAP I've done is a tip for Pedometer++, and it seems like he had links to each option, which is very much like the prereq IAP stacking I mention, below.

The absolute worst case here is that we have to ship two apps painfully grafted into one. I've heard horror stories (particularly from the Core Intuition folk, if my memory serves) of moving from long untouched libraries to the recent ones, keeping old versions of XCode and OS X laying around to do emergency patches, and other related issues. I'm not sure you could ship some of this obsolete stuff easily in an app that's targeting 10.11.

Which brings me to the other upshot of this plan...

Eventually you're going to give it away for free

Maybe not in 2017, but at some point, maybe three releases later, I'm going to give everyone what's in EmailClient 2017, and give it to them for free. That is, you don't want an IAP list that says, "EmailClient 2017 Features, $10 -- EmailClient 2018 Features $10 -- EmailClient 2019 Features $10". If they didn't upgrade in 2017 or 2018, sort of like the license crackers, are they really in your market? They paid once. Why not give them a two year-old set of features?

And there are going to be times when it doesn't make sense to keep bundling together Frankenstein with the original parts. Occasionally, you might need to update EmailClient Y-1 to keep the tech stack in line with this year's EmailClient Y, but now that releasing grafted upgrades is your actual strategy, you can code defensively so that you minimize code that goes obsolete next year. Sometimes it's not going to be worth the trouble to make Y-1 play nicely with Y, and you'll be stuck. Let's just hope that, for the most part, your code from last year doesn't go obsolete within 24 months in impossible to fix ways too often.[4]

I'm not sure if you can "stack" IAPs[2] -- that is, require that users have EmailClient IAP 2017 before they can see EmailClient IAP 2018. I'm guessing you can't create "prerequisite IAPs", or I would've heard of a game doing it somewhere. "Want 400 gems for $5? You can, but first you've gotta buy 100 for $10!" Or worse: "$20 to unlock 1000 gem packs for $1!"

But not having IAP prerequisites isn't that bad, is it? And if things ever get so gnarly that you don't want to give something away for free, well, you have other options.

  • You can pretend you're right back where we started, which is the worst it gets, and decide to release a new "EmailClient 2019" SKU.
  • You can get clever and ensure that the features from 2019 can be used independently of features in 2018 (throwing in the subset of '18 features required for '19), and let users buy either. Which is to say that...
  • You can have an IAP list that looks like instructions to a Rube Goldberg machine.
  • You can get even more clever and hold back some features unless both 2018 and 2019 IAPs have been purchased. (Make sure your remind your 2018 & '19-only folks that's the case!)

There are nearly limitless ways to approach this problem. And, again, that's what brings us to the keyboards each morning, right? Fun, clever solutions to real-world problems. I mean, heck, Overcast already figured out how to perform an end-run around trial apps. Why can't we do the same for paid upgrades?

Reviews

Which is not to say the MAS doesn't still stink for new releases. This ycombinator comment in particular pains me:

But on the Mac App Store it takes months for an average application (read: one that is not permanently featured by Apple) to acquire said 5 ratings.

So you think twice about pushing out an update to your app when the current average rating is 4 or 5 stars. Because once your average stars are gone, your app doesn't look very different from any other 0 rating app... and it's pure luck if the user likes your icon enough to click on it. (Which measurably impacts sales numbers).

I see this with my own software... I once made the mistake to update a well rated app and sales plummeted over the next few months until the app could re-acquire 5 ratings to show an average.

I can see that. I wonder what percentage of folks review what kinds of apps. I have no idea.

I guess this just gives more weight to the lesson I hear everywhere: You have to advertise outside of the MAS to sell MAS apps. The MAS is one and only one of your storefronts (was it Liscio who built up this metaphor? He's been everywhere (#70) recently). You send trucks full of software there, but you're still responsible for convincing people they want to buy. If you count on Wal-Mart's shelf placement to sell your goods, you're not doing enough to grow your business.

Which is not to say we shouldn't keep challenging Apple to make the stores better. Which was Counsell's point all along, I think.


(Btw, if this is the first time you've read my blog, there's a decent, just under 110%, chance you won't want to read everything, which is pretty heavy on "notes to self" style posts. You might prefer to watch just the indie tag instead.

Unless you're Sam Soffes, in which case, if you're not as laid back (in that good way) as you sound, you might take this post badly. But then you could destroy this site's beautiful design, basically unchanged since 2001, and make up for it pretty quickly.)

Yes, the parenthetical in the title is supposed to remind you of Loser.


[1] I think it was Charles Perry's AltConf talk that had some number like 0.01% of surveyed iOS App Store authors considered their apps financial successes. If you ask me, that's a pretty good explanation for all the abandonware. That and the fact that as the store progresses, by definition there are going to be more abandoned apps. I'm guessing there are very few resurrections. So don't get too down on the app store yet! (Perry's slide is below. Go watch his talk, and then a few more from AltConf too!)

[2] Now is as good as any to admit I'm a C# guy just starting to write for OS X. Yes, I'm using Xamarin in the hopes that makes going crossplatform much easier. That said, I've used Macs for years, and have even contributed to a few Mac sites and a [trivial] piece in the Mac Bible's 6th edition, but in many places here, I'm talking without platform-specific knowledge of what the implementation would require.

[3] I've been told that a lock is designed to keep an honest [hu]man honest. I think that's fair. We're doing the same thing here, because, honestly, what percentage of crack-artists were going to pay for your app? It's not zero, but it's also not close to 100%.

[4] If you find that your code is going obsolete every 18 months, you might need to reconsider your dev workflow. ;^)

Labels: , , ,


posted by ruffin at 8/19/2015 02:03:00 PM
Friday, June 26, 2015

A good, thought-provoking post from Actively Lazy today:

I think we massively underestimate the cost of coordination and communication when building software.

That's true. Full stop. This, the communication price, is exactly why folks are still quoting The Mythical Man-Month. Communication makes [hu]man-months mythical.

But what we do with this discovery is what's really important. It's easy to find one solution and think it's the only solution, as I believe Actively Lazy has done. Let's explore two.


Pair Programming

Here's Mr. Green's (Actively Lazy's author's) take home (emphasis mine):

From the outside its very easy to miss: a quick 5 minute conversation laden with jargon. And yetโ€ฆ this is where the magic happens: this is where the design comes from. But if that 5 minute conversation interrupted someoneโ€™s work, the next 45 minutes could be lost while they try and reload into memory what they were working on. Pile up a few of these interruptions in your day, and no wonder it feels like youโ€™re swimming upstream. [anybody else hear Joel Spolsky now? -mfn]

Clearly, what we should have been doing but werenโ€™t was pairing. That way there would only have been one narrative thread. One sequence of ideas being applied at a time. ... There is no cost of interruption when youโ€™re both already there, immersed in the problem. ...

So in fact: two people can be more productive than one. Two people pairing is definitely better than one person working on their own. ... Itโ€™s all based on a false assumption: that two people working on different machines are twice as productive as one person working alone. Once you realise that this assumption is fundamentally flawed, the โ€œcostโ€ of pairing evaporates. Instead pairing removes the cost of coordination between two developers: no interruptions, no divergent ideas, no merge conflicts.

My quoting makes the argument a little worse; you really should read the entire post in context. But you get the picture. He's arguing that pair programming pays the "coordination and communication" costs as-you-go so that you don't have to pay the piper in spades, to mix some metaphors, later.


Pair programming trades function for quality

When you work in pairs, you create less working code that is higher quality. This is nearly a truism. If you're somewhere code review's benefits aren't appreciated, run. I'm not saying you should require or even use a lot of code review -- my jury's still deliberating on its best use -- but reviewed code is of higher quality than code that isn't.

  • Best practices
    • Test Driven Development
    • Linted code
    • Reviewed code
  • Efficiency/Scalability
  • Normalization/smart reuse
  • Standardization
  • Proper error handling

At the same time, that "high quality code" may not necessarily do the job better-qua-[characteristic X]. Just to get your started, here are a couple of common Characteristic Xs (feels like we're making Powerpuff Girls)[1]:

  • Be delivered on time (this is the biggest argument contra-quality
  • Be significantly faster (when n=1, does the fact some schmoe used a bubble-sort matter?)

This is why iteration is such a catch-phrase for coding (though also see its abuse, here).[2] Get 'er done, then, if you have time to notice (or are forced to notice by poor performance), but only if you have time to notice, make it better.


Quality vs. Functionality

So is pair programming the answer to removing communication problems? Sort of. Depends on why you think you need to communicate! One key here is that Green only has two developers:

Me and one other developer have been working on a new, greenfield project.

If you only have two devs, of course pair programming kills the problem of deferred communication. But if you have 40 devs, you've now got 20 pairs that could need to sync back up.

Twenty pairs is still much, much better than 40 individuals. Twenty pairs should also be much, much better than 20 individuals. The same way letting someone use your alpha version for 15 minutes uncovers bugs simply because they have a different mental model than yours, letting another strong developer take a look at your code will unearth some low hanging fruit (man, I'm mixing metaphors today. Uncover some barely buried tubers?) quickly. And even that single filter means your code will require less change to integrate with the rest of the team.

The key take-home is that pair programming makes for higher quality software, not more of it. There's more than a single metric for evaluating software's worth.

The other side of code is functionality, often reduced to the reasonably useful metric, lines of code (loc).

Aside: If used correctly, lines of code can give you a general idea of how productive your devs have been. Though insert the usual caveats:

  • loc is really only useful comparing a specific dev's output to her or his own
  • a stark increase in loc for a dev often means they're pumping out code too fast, and feel stressed
  • brilliant solutions don't always produce lots of code; usually it's the reverse

And there's a performance bar that tells us if code is good enough. That is, pair programmed code may be higher quality, but it may also not be significantly faster or use significantly less memory, etc. The biggest problem in evaluating how much quality you need is that often you don't know how significant a bad LINQ statement might be until you run it at scale.

I think the suggestion is that you have to bias your development culture so that it always strives for high quality with respect to best practices. You want good scalability? Don't overuse ORMs. Don't push logic into the client. Write good SQL. Take time to plan your schema. But I don't know that pair programming, sacrificing half your speed for code quality, is necessary if you have this high-quality culture instilled with a team of well-hired developers.

You get the point. The bottom line: Your code is higher quality if it's reviewed for standard practices, errors, and efficiency/scalability first, even if that review time reduces how much functionality you can build. But remember that functionality is why you're in business: you shouldn't sacrifice function for idealistic quality. In other words, you need to discover...


The nasty truth: There is "good enough"

Imperfect code can still provide acceptable functionality. News flash: There is no perfect code. [Most] Any solution is beyond a certain level of complexity is subjective.

That there are many subjective Right Answers to complex problems, and it's precisely because you can only reasonably select one of them that you ask for advice before tackling them. That is, subjectivity is why you design code before coding. Even when you're going to tackle a major problem alone, you "pre-[re]view" with someone else (don't you?!!) your selection from all the different ways you [both] can think of to solve it.

The reason you consult someone else first is because you know the problem you're working on needs an extra helping of quality mixed in with the quantity. You slow down, sacrificing not just your but your teammate's output, to ensure your output's quality. Your know your solution -- or at least its design -- needs a second set of eyes. In a sense, though you may not have touched a keyboard [much], you are already pair programming.

The conflation of these two code metrics -- functionality & quality -- is what provokes Mr. Green to say...

Unwinding a few days, we probably would have got more done with just one person working. That way there would only be one narrative thread through the code, one sequence of refactoring steps at a time.

Wait, what โ€“ say that again: we would have got more done last week if only one person had been working on it.

Sure, one person working alone could bring the code back to a single narrative, but it'd be one person's narrative. You've right back into communication debt. Hopefully it's not as bad as it was before you'd code reviewed with your coworker, but the new communication debt again exists. This single person's progress is another unreviewed revision.

And perhaps that's good enough.


A different lesson: Seperation of concerns

The real key is that you can't have X people, where X is determined by your company's management, working on the same code at the same time without factoring in the communication costs for getting X folk on the same page.

If you don't have time to factor in communication and code review, the new code will be worse in all of those standard ways -- best practices, efficiency, normalization/smart reuse, standardization, error handling. Worse, each person will find themselves coding around or being hamstrung by changes to their narrative made by X-1 other coders. Codebases shared by X devs without coordination is worse than having X coders doing their own thing. You do not get X-times (or "Xx" -- I regret my use of "X" at this point) the work, even if each dev is working on their own seemingly independent story!

To get "Xx" functionality (and that's what the company wants, man. When you feel pressure to go faster, and are considering adding more devs to finish sooner, it's because you want functionality), you have to separate concerns perfectly.

  • You must create interfaces ahead of time, at least one per "communication unit".
  • You must hit these interfaces.
    • Rather, if you change them, you have to change your schedule drastically.
    • You just changed two team's narratives!
  • You must create test cases for these interfaces with realistic values.
  • The interfaces must be natural breaks; they must be truly separate concerns.
    • You can't have any interaction between them.
  • They require an emphasis on defensive code.
    • There can be no unintended interactions between the opposite sides of the interface.

The interface is a contract. And objective contracts (strangely possible in code; it's magic) are the most efficient means of communication for software projects.

Now you're creating functionality as quickly as is possible. And if done correctly, you can get a lot closer to the myth. But there's a huge, obvious downside...


Communication Debt added to your Technical

And remember, if X is large and your stories are done by one person per story, your "communication debt" will be just as huge. You will have technical debt, and you will have a huge learning curve for the new dev if the person working on the code changes. And if the meatware half of the cyborg leaves your company before the knowledge transfer/code review takes place, you're in trouble.

The good news? This debt will be firewalled by the interface. That's as far as the bad can go, if you have smart TDD.

If X is large and your stories are done by two people, the debt will be more than halved, but your functional output could be more than halved as well!

Quality code is hard, (c) 1842.


[1] If you want an example of folks arguing against higher quality code for short-term preference Characteristic X, see the constant complaining about JSLint rules wherever JSLint is used. Let me summarize two-thirds of the answers on the JSLint tag on StackOverflow: "If you don't like rule X, you should use JSHint and turn it off." JSLint is a form of code review that emphasizes a set of "best practices". If you're left to your own devices, you might not always follow them. Following them might not make for better code in every situation, but you will have good, standardized code with fewer errors. JSLinted code is higher quality code, though conforming takes more time than not.

As I've said before, "If I had to inherit legacy code sight unseen, and could only require that it be JSLinted or JSHinted (pick one), I'd pick JSLinted code every time," and that's because it's higher quality.

[2] Hey, look! I've talked about Agile (here regarding documentation) as early as 2004! Three cheers for ourselves!

Labels: , , , ,


posted by ruffin at 6/26/2015 10:20:00 AM
Friday, June 05, 2015

jQuery, the Rosetta DOM

I've recently been rolling a node project essentially from scratch, largely so that I can actually learn Node, but also largely due to my aversion of introducing unnecessary libraries. Ever since the first ADO wizards for VB6, all too often libraries do the first 80% of what you need in record time, but getting the last 20% (which can be features, but is often also optimization and bug squashing/code-arounds) done takes more than twice the time you initially "saved". And except for what was just short of a two-day rabbit hole into returning gzipped files (I decided I should gzip on the fly, which is cool, but not mvp), it's gone almost painlessly.

Over the years, I've found that you usually have to be proficient enough to write your own library to use an external one mindfully, and that time, imo, is often better spent writing code that targets exactly your own use case instead. Nobody knows your pain points better than you do, and nobody should know how to take them away better either.

Well, I've often admitted one exception to my library aversion in client-side Javascript [1]: jQuery. Even I (circa 2008-2013) thought you'd have to be an idiot not to include jQuery by default.

Recently, though, even my jQuery love has waned. See http://www.sitepoint.com/jquery-vs-raw-javascript-1-dom-forms/ for some context:

The debate is still raging [he does have 74 comments currently -mfn] on my recent Do You Really Need jQuery? article but, in summary, there are two reasons why using jQuery is a good idea:

  • you need to support IE6/7/8 (remember you can't migrate to jQuery 2.0), or
  • without jQuery, you'd spend longer writing a jQuery-like library than developing your application.

Or, as Rob Niedermayer (I'm assuming) says on knockoutjs' website...

Everyone loves jQuery! It's an outstanding replacement for the clunky, inconsistent DOM API we had to put up with in the past. jQuery is an excellent low-level way to manipulate elements and event handlers in a web page.

And here's a quote from the "Do You Really Need jQuery?" article:

jQuery is an Abstraction

If you think web development is tough now, try developing JavaScript code in a browser from five or ten years ago. The typical problems:

  • DOM navigation and manipulation differ. For example, Firefox (correctly) included whitespace in the DOM, IE6 didn't. Therefore, you couldn't depend on node.firstChild returning the same thing.
  • Ajax was natively supported in most browsers, but an ActiveX control in IE (even though Microsoft invented XMLHttpRequest).
  • IE had a different event model but even the other browsers had their share of inconsistencies.
  • CSS2.1 support varied immensely.
  • Animation could be difficult especially when coping with box model differences, form controls and iframes (IE6 placed select boxes and iframes above everything else on the page).
  • Developer tools were rudimentary. Prior to Firebug, Firefox's error console and IE's clunky modal error box were the only built-in browser tools.

Developers had to write abstraction functions to get around these issues. jQuery, Prototype and MooTools evolved from the primordial chaos; the libraries smoothed over the cracks and provided useful missing features such as DOM selection from CSS selectors.

The title to that section really is the take-home. jQuery is an abstraction layer to make fairly different browser object models look the same when you're coding. jQuery was a Rosetta DOM for browsers in the 1990s and 2000s.

But if you've spent much time on the Mozilla Developer Network, you'll notice that the browser compatibility sections are starting to look a lot less nasty. Non-IE browsers have had good standards support for a few years now, and if you can limit IE to 10+, well, I'm not sure you do need a Rosetta DOM jQuery any more.[2] I mean, when we get to the point that someone gains tons of traction for posting that Safari is the new IE, we know we're getting somewhere.

Now look, jQuery rocks. It's, to use "rocks" as many times as possible, rock solid in its rockery. I really like using it, and, during a job interview a few years ago, when asked about Javascript libraries, I said jQuery has a special place in my toolbox where I consider it as good as a first-party lib. It's that solid, in my experience (vs., say, ExtJS 2 or ArcObjects in the 'aughts. Man, those were the bad old days.).

jQuery, the gateway drug

So I'm not opposed to jQuery's use, except that I've slowly come to see that jQuery is a gateway drug. I've talked about overuse of client-side templating here before (the only one I can find right now is this one; surely I've spilt more pixels than that), and I'd argue that the situation my team found itself in there -- where, as a company, we had a system that nobody bothered to test at scale on IE8, our minimum sys requirement, and our KnockoutJS-based system was completely dead on that platform a few weeks before release -- was enabled by our overly comfortable approach to library use.

Heck, I'd consider recommending not using jQuery simply because so many libraries depend on it. Without jQuery, you can't easily slap in everything and the kitchen sink.

Unfortunately (he said only half-jokingly), Angular and other templating libraries have their own mini-libs that will gracefully step in if jQuery isn't used, so you can still get into trouble if you're not careful. Here's my quick 2ยข on templating: If you have data-stores and business logic on the client, you're doing it wrong. It would be interesting to figure out how often Google's folks use jQuery with Angular, and how often they limit themselves to jqLite. In a sense, creating jqLite for Angular is exactly what I'm proposing here.

But if you require folks know how to write standards-compliant Javascript, it quickly becomes clear that your company's culture prefers writing code tailored specifically to your set of problems rather than grabbing something off the rack you're going to be hemming and patching for life.[3] Over the long term, especially if you don't have legacy code and legacy browser users, you might find you save a lot of time and money.


Aside: Which brings me to another disappointment that I'll compulsively mention briefly -- if you do use an open source library, for heaven's sake contribute back to it. You will find problems. It's useful to make defensive patches to it that don't touch the original if you think your "issue" is too specific to your use case, but it's also important not to let the concept of a third-party lib hamstring your development as if the code was untouchable.

We used NHibernate at a previous job, and had so much invested in a certain release that we were scared -- rightfully, I'd argue -- to upgrade. But why we wouldn't branch that old version and start releasing fixes we knew were needed, I have no earthly idea. Though I think I argued successfully with the folks whose opinions mattered [to me] that we should branch and extend, I still consider it a huge failing that I couldn't reach critical mass there where we actually made stories and improved the library.


Update: Looks like I'm not alone thinking the library soup needs to end. Interesting post from Allen Pike, which also touches on Angular breaking backwards compatibility with version 2. It's bad enough how quickly these things go by the wayside from neglect. Do popular ones have to die too? Of course, the bottom line is that if you'd stuck to vanilla, standard-compliant browser-based JavaScript (or minimalist libs), you'd be completely (to very) insulated from moves like this. And, as I mentioned in the aside, if anyone Does the Right Thing, Angular 1.0 will continue to live well past the team's leaving it, if that's what they plan to do. That's the proverbial beauty of open source.


[1] There are certainly other libraries I don't mind using, and some that get close to this "good as a first party" status, but they're few. I really like MailKit so far on .NET, for instance. And of course I like "real" dbms systems. But I'm library adverse to the point I always have to double check that I'm not exhibiting NIH syndrome.

[2] I realize this is easier said than done. But places that have to target IE9- are being driven by corporate clients whose workplaces are stuck on Windows XP, I'd wager. The usage numbers suggest that IE10+ is well over half of all IE use, and that leaves less than 7% of users worldwide that need to learn how to install Chrome to their Documents folder.

[3] Yes, I realize you'll be hemming and patching the bespoke stuff too. Hopefully you'll still suffer the analogy.

Labels: , , , ,


posted by ruffin at 6/05/2015 11:01:00 AM

<< Older | Newer >>


Support freedom
All posts can be accessed here:


Just the last year o' posts:

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ยข, & ? if you're keypadless


Powered by Blogger etree.org Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.