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