Saturday, September 26, 2020

Fail Faster. Follow the Fun.

Marc "MAHK" LeBlanc on game design: 

"Fail Faster. Follow the Fun."

That quote via this video:

I'm proud of the little games I've made but sometimes I wish I had fleshed out more of them... most are just barely enough wrappers for playing with a lovely little mechanic.

awesome modern css layout tropes

Excellent video, "10 modern layouts in 1 line of CSS"


I think the name is mildly misleading (the 1 line is the core idea but you'll still have other layout work to do) but it's great stuff! It has an accompanying website with CodePens.

Thursday, September 24, 2020

the importance of rapid iteration and proximate feedback

A couple things I've been experiencing (reading, listening to, and programming) are having some synchronicity:

Daniel Kahneman, talking with Sam Harris on what it takes to develop intuition - to impress knowledge into your immediate, System 1 processing vs your slower, rational System 2:

Now, in order to recognize patterns in reality, which is what true intuitions are, the world has to be regular enough so that there are regularities to be picked up. Then you have to have enough exposure to those regularities to have a chance to learn them. And third, it turns out that intuition depends critically on the time between when you're making a guess and a judgement, and when you get feedback about it. The feedback has to be rapid. And if those three conditions are satisfied, then eventually people develop intuition. 

Maddy Myers talking on the podcast Triple Click on Which Games Do We Wish We Were Better At? - specifically at around 16:00, on playing at local competitions for fighting games

And then hearing from people, in person, feedback on what I was doing wrong and then using that immediately to improve...you really can't beat that [...] because you're getting that hands-on lesson constantly... and matches are short enough that you can get actually quickly implement that! Which I feel is another reason why I could more immediately see improvements there then at something like Starcraft, where I feel like I did 600 things wrong, and the next match is going to be completely different than this, so who frickin' knows! - But at least in fighting games you can change one thing and see a big difference and that always worked better for my learning style.

And I'm back on my own, why does my System 1 have a such a dislike of unit tests and disinterest in locking down javascript via typescript, when so many other smart developers love both so much? 

I think I answered this question on this blog in 2016:

I've found SOME parallels in the way I write code relative to the Proper Unit Tester (PUTter). I'm a ridiculously incremental coder; I hate being "wrong", and kind of scared of making a block of code so big that when I'm finally ready to run it I don't what part might be going wrong, and so I've always relied on tight code/run/eval loops... lots of manual inspection and testing of assumptions at each little baby step. Often I'd even write little homebrew test functions for the various edge cases. The difference between old me and a true PUTter, then, is I would then throw away those little functions and logging statements in the same way a building site takes down the scaffolding once the building is standing. But PUTing says - look, that's how you know this code still works in the future, nurture that scaffolding, keep it around... make it survive any refactoring, fix all the mocks when the endpont input or output gets added to, etc etc.  That's a huge cost, and I'm still trying to convince my self it's worth it.

TypeScript is a bit of friction on that kind of iterating axel... it tends to violate DRY; if you are messing with the interface of a function, and especially if your typescript system is a bit hair trigger (like yelling at you for unused values) you might have to make changes 3 or 4 places. Yeah, it's better than undocumented "oh we assumed this random key was in the props object but forgot to tell you", but it's also not much of a replacement for updated real documentation. 

Unit tests can be similar, especially for UI. I'm not sure if I've just never landed in a place that was doing it right, but every place I've been at, the correct response to "oh the unit tests broke!" is not "time to fix the code!" but "time to update the unit tests!" In general, basic developer testing confirms the new feature is working, and the old functionality isn't blatantly busted - and unit tests are often awkward enough to set up (in terms of mocking an environment for the code to run in) that they are almost always more fragile that the core code... they are canaries in the coal mine that keel over if a miner passes wind.

The other thing about UI is that dependencies often aren't well described. Today we ran into one where a change to the CSS line-height property of a container label was making our checkboxes break. There wasn't really a way to capture in that in a unit test, unless you were doing screenshot-y ones. (We use text-based snapshots... a few years back I interviewed at one place that was using graphical screenshots which might be more revealing) 

A Unit Test is setting up a fragile glass ant farm to make sure a few little ants are still toiling away - but that's no way to test the health of the colony, and I don't see why so many developers don't see the costs of them in terms of developer time, both at the outset and as the codebase extends. 

The errors that do show up as a codebase extends are emergent... most well sized units are too small to fail, the trouble happens at higher levels. And yadda yadda, what if you have to refactor the unit, and want to make sure it works? Again, the number of subtle problems I've seen introduced with that kind of unit refactoring in real life I could count on one hand.

typescript and style components gotchas

Sometimes typescript + style components make a hot mess of build errors as you pass around various interface-y objects. This article outlines some good practices, like how you can define the props your styled component is using:

type ButtonProps = {

  primary: boolean;

};

const Button = styled.TouchableOpacity<ButtonProps>`

  opacity: ${(props) => (props.primary ? 0.5 : 1)};

`;

Monday, September 21, 2020

understanding package-lock.json

A coworker recommended this article when I was trying to figure out why package-lock.json should be checked in.

I could see the appeal of package-lock.json as a snapshot thing, so you could trace back and recreate an environment in production or whatever. But the idea of pulling down another dev's updates that might have changed both it was and good old package.json was weird... like the latter is "what the versions should be" and the former "what the versions actually are", so until you run npm install, you're kind of living a lie?

In short I was trying to reconcile other documentation that made clear 

One key detail about package-lock.json is that it cannot be published

with this newfound passage:

The idea then becomes that instead of using package.json to resolve and install modules, npm will use the package-lock.json. 

I guess npm using package-lock.json to tell it what to install is the weird/new bit for me... so npm says "I will install what's in package-lock.json, knowing what needs to be installed by comparing it to what's actually in node_modules... unless package.json needs something newer, in which case I will install that newness and update package-lock.json accordingly"...

I guess it makes sense but it feels a little weird, like there's not quite a single source of truth for stuff.


Friday, September 18, 2020

mocking fetch

Had a rough week with unit test. The status quo of unit testing in React seems a little rough; the fake-y environment you set up to fake being a browser seems at least at delicate as the actual code you want to test...

I had almost forgotten discovering react-testing-library which seems to be the future, and a little closer to unit tests that simulate users rather than Enzyme's way of getting lost in a see of props. There was some learning curve though; the finders like screen.getByLabelText() and screen.getByRole() seem designed to help get devs thinking in terms of a11y issues (like making sure aria roles are accounted for etc) so if you're used to thinking "oh I'll just grab it via the element id".... it kind of discourages that sort of thinking.

I had a surprising amount of aggravation faking the client/server response. I was hoping code like this would set up Mock Service Worker as a nice little endpoint:

import { rest } from 'msw'
import { setupServer } from 'msw/node'

//... later in the test: 

const server = setupServer(
  rest.get('/endpoint*',  (req, res, ctx) => {
    return res(ctx.json({ results: ['abc','def','ghi'] }))
  })
)  
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

Though I'd also have use a polyfill for fetch

const {Response, Request, Headers, fetch} = require('whatwg-fetch');
global.Response = Response;
global.Request = Request;
global.Headers = Headers;
global.fetch = fetch;

But I couldn't get it to work; somehow the fetch().then would never resolve. It's still easy to get trapped up on this asynch stuff!

So after banging my head against it I went with the less recommended practice of handrolling a stubbed out fetch:

const mockSuccessResponse = {results: ['abc','def','ghi']};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
  json: () => mockJsonPromise,
});
global.fetch = jest.fn().mockImplementation(() => {
  return mockFetchPromise;
});

//..and later
global.fetch.mockClear();
delete global.fetch;

Blargh! Good enough for the moment I guess.

Tuesday, September 15, 2020

Saturday, September 12, 2020

the ux of scroll direction: natural and y-axis: inverted

I recently reconfigured my work-from-home desk to have my laptop on a stand - it raises the webcam to a more flattering level, makes its screen easier to glance at (next to my main monitor, rather than beneath), and keeps my wrists from the unpleasant heat of a warm machine.

I switched to a plain old mouse after a failed attempt to use a "magic trackpad" in a laptop-like configuration. Macs are frustrating though because they have a single "scroll direction: natural" setting that applies to both mice and touchpads - but I want exactly opposite behaviors for the two.

The "Natural" setting makes the touchable surface of the trackpad act like the surface of an iPad: if the image on the screen were of a piece of paper, it's like you're dragging the paper with your two fingers - so to see the lower part of the paper in the glass of the screen, you drag up. 

But historically, mice scroll by dragging a little marker in the scrollbar, and in the 90s mice added a physical scrollwheel (it's so nice to have that physicality back with my cheap bluetooth mouse - though occasionally frustrating since I can't easily scroll left and right with it.) Anyway, with that scroll marker, moving up moves the viewport up... so if you want to see the "lower part of the paper in the glass of the screen" you drag down.

But Apple treats this as one setting! (I'm not sure what it feels like to use Apple's "magic mouse" that has a trackpad-ish surface...) There are programs that allow you set the two differently, but the hack I installed long ago has noticeable lag where a scroll starts moving the wrong way and then corrects.

There's an interesting parallel to life in video games, especially 3D run around and shoot games. The "standard" controls for aiming the crosshairs make a certain amount of intuitive sense: if the target is above where you are aiming, press up on the controller to aim at it. But for a certain population of gamers they will immediately head to settings to reverse that y-axis - they want to press down to aim at the higher target.

This diagram attempts my best guess as to why:


The first example, "normal", shows how the controller (represented as a compass rose) is mapped to the tip of the gun. So to aim at the higher target, you press up (and the character in the viewport moves down).

Gamers who prefer inverted controls do a different mapping... it's to the viewpoint, or you could say the top of the avatars head. To hit a higher target you pull down - or rather, back, and the viewport slides up.

The third sketch shows how this is akin to how flight simulators or flying games in general work - a pilot pulls back on the flight stick (which corresponds to down on a more humble controller) and the plane tips back and ascends. 

I find this difference in expectation among gamers fascinating (and frustrating; some games, like "Luigi's Mansion 3", don't bother to put in a control scheme to accommodate the "inverted" preference.) I think gamers who grew up with keyboard controls, mouse aiming will hardly ever long for inverted-Y controls

And there's that parallel with the mouse/trackpad scrolling: my girlfriend thinks I'm weird for ever using "natural" scroll for my Mac trackpad - I'm guessing her model of it is based on the scroll position and/or the viewport, rather than the content.

And they say there's a similar ambiguity in how people map space and time - does "we had to push the meeting forward 2 days" mean the meeting is now earlier or later than it was planned? Your answer may have to do with if you see yourself as fixed, and time heading towards you, or time is fixed and you are travelling through it... and some cultures have entirely different ways of mapping the two.

Fun stuff in UX! Don't assume your audience maps space the same way you do...

Friday, September 11, 2020

ui and testability: the ant farm, the GOTO statement

I have never been a true aficionado of unit testing; 

  • I've seen too many that test implementation and not true functionality and that break even though the underlying code is fine after a refactor
  • as a form of "documentation" they aren't particularly easy for a human to read and glean information from (though I admit they are more constrained to be updated along with the code, unlike external documentation)
  • when written by the same coder who wrote the code under test, they assuredly won't test much that the coder didn't think of when doing the implementation, and most true small units should - frankly - be "too small to fail".
  • when implemented and providing high code coverage, they're sometimes cited to give a false sense of security

But mostly, especially for UI, the bugs I've seen that make it to production all depend on context; they're an emergent property of interacting parts that don't properly understand each other enough to play well; 

  • the unexpected format of the response from the server - (oh yeah, the format in the db and endpoint updated 2 sprints ago but we forgot to update the code and the tests!)
  • the odd browser incompatibility for a CSS or even Javascript syntax
  • the surrounding DOM of the page leaking into the embedded unit and doing odd things
  • the text value from the server (or the user input) was much longer than the happy path examples on the designers happy-path mockup and things look weird
  • a surprising race condition or other sequence-based context-y problem
None of these issues are particularly amenable to being caught early by the isolated confines of a unit test. (And half of them aren't easy to see if your test runner output is based on a command line and not human inspectable UI.)

When you make up a unit test, it's like you set up a tiny 2 or 3 ant ant farm and carefully observe your ants correctly doing their own thing in those glass walls, and sure, it's important to make sure your ants are basically healthy - but there's only a small chance that that's how you're going to catch the problem with all the ants of the colony ahead of time.

The sweet spots for unit tests are in functional programming: ALL relevant input is known / there is no other reliance context to botch things up, a manipulation of that input  - complex enough to be worth testing! - is performed, and the results can be inspected in their entirety. And one reason why declarative programming for UI has gained so much popularity is that of all styles of UI code it has the highest chances of meeting that criteria... but even then the tools for making a component, faking an interaction with it, and seeing the result are just so-so at best. 

Anyway, I didn't mean this to be quite this long of a ramble - mostly I wanted to set up this quote I snagged in 1998:

"Menu items are the modern programmer's way -- even that of the Java programmer, who is too pure of heart to use pointers -- of putting an obscene number of unpredictable GOTO statements everywhere in his code." -- jhayward@imsa.edu via Usenet's "rec.humor.funny"

 Very old school in both tech and gendered language, but I think about that in the context of UI a lot.

Tuesday, September 8, 2020

safely unpacking nested key/values in javascript/ECMAscript - the mess of the null propagation operator

 So if you have an object like 

const foo = {
  bar: {
    val: 'got this!'  
  },
  apple:{
    banana:{
      cherry: 'fruit'
    }
  }
};

and you want to safely get foo.bar.val or check on foo.baz.val without getting errors... there's a good syntax for it based on question marks - but isn't yet part of the the standard: foo?.bar?.val or foo?.baz?.val - it's scary, because it seems to work in some browsers, but isn't considered standard.

I made up a code pen that says something like this function might be an ok substitute, if less clean:

function getNested(obj,...keys){
  keys.forEach((key)=>{
    if(obj === undefined || obj === null) {
      return;
    }
      obj = obj[key]; 
  });
  return obj;
}

Then you can call getNested(foo,'baz','val')

Sunday, September 6, 2020

the return of the obhack

Back in the 90s I was often on Usenet - a distributed set of conversational message boards. The level of discourse was generally pretty high, maybe because of the system's academic routes and relative lack of anonymity, and you could use whatever newsreader you liked and your single account to interact with all kinds of topic-based groups - kind of like Reddit, but more geared to writing in paragraphs rather than sentences.

Besides "rec.games.video.classic" and "alt.fan.cecil-adams" my favorite group was "alt.hackers" - hackers in in the sense of "making cheap and cheerful kludges" vs "hacking into computer systems". (Or the MIT sense of "grand stunts") It was "self-moderated" - there was a specific technical trick you had to pull in order to post there. The "netiquette" of the group suggested you post an "obligatory hack" or "ObHack" if your post would otherwise be off-topic.

I kept a list of my own ObHacks for future use, and there's still a part of my brain that thinks "oh, that was a clever little thing I came up with, I should file that away for an ObHack!" So here's two of those about the lockscreen wallpaper for my phone.

One is for band: I take a snapshot or screenshot of the setlist and then post it as my lockscreen, so I can quickly see what songs are coming up without fiddling with my phone too much. It's not quite as classic as taping a list to the floor but it's a lot less work.




(Plus then afterwards I have a nice memento of the concert until I remember to put my wallpaper back...)

Anyway, I noticed that changing wallpaper and case is a nice way to make a phone feel refreshed and new, so I got this bright yellow case to replace the blue silicone one that was starting to wear. I decided to lean into how "bumblebee" like it made my phone look and made up this wallpaper:

The slight ObHack cleverness was using a screenshot to layout where the bee appeared - I wanted it so that even if a song or podcast was playing, I could still see the bee, so I took a screenshot and used it as a layer to get the placement right:


Then I hid the layer and saved. (In practice, the bee was a smidge lower than where I thought it would show up, but I decided on a new shade of blue so I had to redo it anyway.)

Incidentally, Piskel was a lot better at making the pixel art bee (I stole the design by googling "pixelated bumblebee" and seeing these cool Etsy earrings)
 




Friday, September 4, 2020

jmespath - query syntax for json

I hadn't heard of jmespath before this slack msg:
Haha my coworker said she is naming her first child Jmes, in honor of Jmespath lol

but I dig it, I've sometimes thought I needed something like that. 

the spatial ux of thinking about time itself

I've always been interested in how humans visualize and put metaphors to time ("life's like an hourglass glued to the table" I've been told) Some of my interest was sparked by recognizing how idiosyncratic my own way of spatializing the course of a year and the course of a week as counterclockwise circles (I made visualization of those timehoops a while back)

All of these are akin to synaesthesia - time isn't inherently spatial, but we find the metaphors for it useful.

This Anthropology.net article discusses a few of those metaphors that were less familiar to me
  • I knew of the Aymara of the Andes, who reverse the more common view of marching into the future with the past at our backs - "the past is known and has been seen, and thus lies in front. The future remains unknown and unseen, and is relinquished to be behind the ego".
  • For many speakers of Mandarin, "the past is referred as above the speaker. And the future referred to as below the speaker." (I'm not sure if the metaphor is that of plummeting? That's almost as morbid as the hourglass glued to the table!)
  • For the the Pormpuraawans of Australia "time always flows from east or the past to west or the future" regardless of the current location and orientation of the speaker.
  • For the Yupno peoples of Papua New Guinea, "time is a topographical concept, time winds its way up and downhill." (The article points to speculation on how this might relate to the group's literally uphill migratory history.)
If you dig this topic, you might like the book "Einstein's Dreams" by Alan Lightman, that posits the great thinker dreaming of different ways times might behave, and how humans might respond. (If time slowed down noticeably with velocity, would people attempt to live their life always in motion in order to have more time, put their houses on wheels and go go go? Or in a universe where time slowed based on proximity to a central point, would adults with young children or old people journey to be nearer that point, while people trying to flee bad memories would head as far away from it as possible?)

Thursday, September 3, 2020

Tuesday, September 1, 2020

workspace noodling: laptop in front vs old school keyboard and mouse

 Random self-indulgent first world nerdery: for a long time I've really liked having a second monitor up above my laptop, using the laptop as the keyboard and trackpad and secondary monitor. It seemed like such a no-brainer, I almost had pity on my coworkers who put the laptop on a stand and needed a separate mouse and keyboard. I loved how the trackpad is right there beneath your thumbs, none of that reach over for the mouse, and just how efficient it seemed in general - and also the elegance of having the same arrangement when on the go or at the desk, so never having to retrain my hands...

Well... I've been having second thoughts. Honestly it's mostly because I was tired of warm wrists from overheated laptops, but also I realized I was using the laptop screen less because it was so low, plus I knew I would get a better webcam angle with the laptop riding higher.

Sadly, I don't think anyone makes a good "keyboard with trackpad integrated in front" (honestly, if I could somehow adapt the new iPad Pro magic keyboard/trackpad combo for use on a Mac, that would be ideal...)

So I bought a laptop stand, grabbed an old Apple Apple "magic" keyboard Melissa wasn't using much, and bought a "Magic Trackpad" to place right in front of- recreating the laptop w/ trackpad-at-the-thumbs experience. But, it was uncomfortable, and prone to accidental hits... even after several revisions of lego-constructed wrist rests:

So I guess I'm back to keyboard plus mouse... I still find myself reaching my thumbs down but there are some advantages to the mouse: the cheap bluetooth mouse I got has a scrollwheel, which is a tactile pleasantness I had forgotten, and a distinct button for right clicks, which Mac still supports, albeit grudgingly. Still, it feels so oldschool to need a mousepad (for my white IKEA desktop) -- it's like I'm back in the AOL generation...