Thursday, August 31, 2017

Idea: benchmark for vdom

What is the tradeoff between using a virtual document object model and straight-forward updating of the dom directly?  Surely, an app that inserts a single div in response to user input wouldn't justify the use of a virtual dom . But what is that tipping point?  What has to happen in the app in order for a virtual dom to become worth the engineering?

Perhaps a benchmark test could start with comparing two apps.  One using a virtual dom, the other not, but otherwise the same.  Compare metrics like time difference when inserting one element per cycle.  Then ten.  Then 100.

This might help:
https://localvoid.github.io/uibench/

Saturday, August 26, 2017

CDNs vs. Bundling and a tool to help.

When creating a website, I'm a fan of using content delivery networks (CDNs) to include libraries, over straight-up bundling all code into a single js file. That is to say, I like putting such tags into the heads of my html pages:

<script src="https://d3js.org/d3.v4.min.js"></script>
One ostensible advantage of using CDNs is to reduce bandwidth overall by caching commonly shared code on individual browsers. Rather than, say, each app loading its own version of d3, for instance, the CDN delivers the library once for each user, and each website references that same code. I imagine this would work quite well with a popular library like jQuery. Another advantage is that it allows the developer to quickly try out libraries during development without committing the time to download and configure those assets. If the idea or the library isn't working, it's easy enough to roll it back by simply deleting that <script/> tag.

However, it's a bad idea to rely on CDNs solely in production code. If the CDN is unreachable by the user, then anything that relies on that code will fail. Hosted fallbacks are important: if a src call errors out, then the browser can load that same asset from the website's server, and the user experience is unchanged. It's easy enough to do. Just follow the script with an if-then check: if the library is undefined, then load the locally hosted version. e.g:

<script type="text/javascript"> if (typeof d3== 'undefined') { document.write(unescape("%3Cscript src='/js/d3.v4.min.js type='text/javascript'%3E%3C/script%3E")); } </script>

Configuring all of this manually can be a bit of a pain, and error-prone, though.  Much easier, and this is the route many devs go, is to simply npm all libraries, roll them all up into a bundle using a task runner, and have a single script tag referencing the entire application in a single js file.  And, heck, I can't really disagree: loading-time of an app bundled like that is much less than the time to load lots of individual files.  It feels less communitarian to do it that way, as it doesn't take into account the overall time-and-bandwidth savings of using CDNs, but I get it.  It's safer.

Still, since I like the CDN approach so much, I wrote a tool to take some of the pain and risk of error out of using them.  

Cdnler is a node app that examines the <script/> tags of input html files, downloads those assets locally, and then outputs the html with altered links and fallbacks.  Basically, it does all of the above automatically.  It's a bit rough right now in the sense that its use case is exactly my own.  Within that use case it works perfectly, though.  I imagine that it could be useful even to folks who like to bundle their entire app into a locally served script, because it will automatically download all linked assets.

I'm happy to expand its usability for other developers, so if you like the idea, but it's missing some ease or usability or feature that prevents you from adopting it, please let me know what that is.






Immutability and Bad Code

I don't often watch videos about coding, but I'm on a jag of it, lately. Picked up a few things on the horizon on the way. GraphQL (an alternative to REST) one of them. CSS Grids another.  (And, as an aside, a fascinating video called Braids in Higher Dimensions). One video in particular I'd like to highlight is this talk by a fellow named Lee Byron at an event called Full Stack Fest in 2016.  It's longish, but if you're a developer on a videos-about-coding jag, too, and it's currently not much past 2017, it might be worth your time.

 

It's a discussion on immutability, not just in user interfaces, but in the entire server-to-user-and-back cycle (He's essentially talking about React without ever actually mentioning React).  But his final words are an articulation of something that I feel many developers, and those who herd them, lose sight of.
MVC and REST did not solve all of the problems that we face in app development. But what I've presented today, also won't. These ideas are going to be far from flawless, and they might not be right for the app that you're building. And I want all of us to continue to challenge the notion that there is one right way to do things.  It's not true. 
I encounter often an almost religious conviction about various frameworks and approaches as talismans against writing bad code or buggy apps.  I have seen people - decent coders, otherwise - unwilling or unable to code without first finding a framework or an approach that other people acknowledge is the right way to do things™. And then, finding this (non-existent) consensus and carefully following all of those guidelines, frameworks and recommendations, they still write overwrought, overengineered, buggy applications that are difficult to maintain.







Sunday, August 20, 2017

Fast & Dirty Minimal Unit Testing

When we coders first encounter the concept of unit testing, it's usually in the context of some kind of instruction: a class or an article of some sort, maybe a video or a podcast. Most often, the instructor will demonstrate the concept of unit testing with the use of a testing framework. In Javascript, that might be QUnit or Mocha. In Java, JUnit. Etc. While a testing framework is fine, configuring it and actually using it can be a bit of a task, especially if you're doing something interesting. Sometimes you just want to code, code, code, not bean count, am I right? You're anointed. You're zooming. Ain't nobody got time for all that!

Friends, I'm here to tell you that you don't need no fancy testing framework to do unit testing! You can test right as you zoom along. Here you go. Here is your essential, no-frills framework:

const doTest= (message, result, expected) => console.assert(expected == result, message, result); // port as necessary to the language of your choice. // or change == to === as you wish.

Use it like this:

doTest("squareIt(10) should be 100 but was ", squareIt(10), 100);

Your first argument is a message ("function should return x"); second argument is literally the function calling some value; and the third is the value you expect to get.  If the expected value does not match the result, then the console will show an error:

Assertion failed: squareIt(10) should be 100 but was 101

Simple. You can open your browser console right now to see it in action.

However, it probably needs one more iteration, because this will fail when comparing non-primitive-type objects like

([1,2,3] == [1,2,3]) == false

a==b
only works if a and b are primitive types.  But remember:  this is a minimalist framework designed to get you testing without interrupting flow.  When you need an upgrade, just go ahead and modify it as you see fit.  If you find yourself needing to compare arrays, for instance, go ahead and modify the doTest. One way to solve this is to borrow yourself a fine array comparator such as:

const isEqualArray = (a, b, i = 0) => a.length != b.length ? false : i >= a.length ? true : a[i] == b[i] ? isEqualArray(a, b, i + 1) : false;

and then modify your unit test function so that  doTest checks to see if the result is an array, and then runs the isEqualArray function.

const doTest = (message, result, expected) => { const isEqual = (result instanceof Array) ? isEqualArray(result.sort(), expected.sort()) : result == expected; console.assert(isEqual, "\n" + message + "\nReceived:", result); };

Don't forget to add a unit test to make sure it's working:

doTest("Comparing the same arrays should pass: ", [1,2,3,4],[1,2,3,4]);

Boom! Your framework now compares arrays.

With doTest in place, it's easy to drop a test anywhere meaningful.  When you find yourself thinking "This function should be returning x but it's returning y.  Hmm." Just  doTest("f(x) => y", f(x), y) Then you can get to work solving the problem with a permanent, reliable partner keeping your goal on track. Later, if some fix has broken something critical, you can pin down more easily what part is broken.

doTest('complicatedFunc("What is going on??") should be "Sssh, Pumpkin. It's okay." but is '...

Also, I recommend throwing this one at the head of your list of tests:
doTest("This test should always fail.", false, true);
This will tell you that your test suite is working as expected and make your heart skip a beat at random moments. Always fun.

As a final note, obviously there are times when you will need a more sophisticated testing framework. But if you find yourself leaving constructing unit tests until the end, give this 'roll your own' approach a try. You can always easily port your tests to another framework whenever necessary.

Tuesday, August 15, 2017

BRC app update

Well, I can say that the 'map' now looks more and more like an 'app'!  Click here to see it.  It has severe UX issues, but it has a rudimentary search functionality, and some responsiveness.  The search bar is in the upper left.  Try it out!

Exciting!

Edit:  D'oh!  It fails in iPad Safari.  That's going to take a minute to troubleshoot.

Sunday, August 13, 2017

BRC map update

Further progress here.  Added art and camps with little labels.  Zoom in to see them.  Also, if you can open the javascript console in your browser (often ctrl-or-option-shift-J), you'll see the data for each marker.  It's obviously still a work in progress, but it's coming along.  While the map is for 2017, the events and camp locations are from 2016.  BMorg will release location data for the current year in about a week.

Friday, August 11, 2017

Black Rock City map update

I finished the SVG map of Black Rock City.  Feels good to get that out of the way.  Now I have a place to put location data such as art, events and camps, from the Burning Man API.  Further, with the javascript in place, it will be easier to make such maps for future years.

Edit:  (The original title of this post was Black Rock City map complete) Ah, it's an old truism in software development never to label something as 'complete', 'finished' or 'final'.  By 'finished' above, I meant only "I has a map".  The project itself is not finished.

I've since added zooming and panning capabilities. To do that, I just grabbed a library (svg-pan-zoom by ariuta)which did the essentials of what I wanted, out of the box.

However, because SVG's coordinate system increases from top to bottom, but real-life GPS increases from bottom to top (i.e. south to north, at Black Rock City, anyway), I had used SVG's transform attribute to reverse the vertical scale.  While this allowed an exact mapping between the map's xy coordinates and actual GPS coordinates, this also caused the svg-pan-zoom library's vertical panning to be reversed, as in airplane controls.

In order to get the vertical panning to work properly, I had to do without the easy fix of the reversed vertical transformation and abandon the conceptually convenient x-coordinate = longitude and y-coordinate = latitude scheme.  The new GPS => x,y  javascript transformation incorporates a vertical flip.  It took a while to get it right, but the gain is that the transformation can be any arbitrary size and origin point.