Wednesday, March 5, 2025

ai yi yi

The tech interview process has been pretty broken for a while. One student made a program to take on the leet code style interviews

Obviously this student's prediction is sobering:

Maybe it’s stupid of me to say this. Most human intelligence work is going to be obsolete in two years. [...] by the time I graduate, these LLMs are going to get advanced enough to the point where there is no significant intellectual work that I could produce value for society.”

I mean, more of the work will be wrangling AI, for sure, and maybe I'm just using AI wrong (or their nerfing the LLMs to save power) but over time I've become less impressed with LLMs abilities to do stuff big picture.



Monday, February 24, 2025

dragging and dropping abandoning jQuery UI and getting a handle on things

 (Sorry, title is a bad joke; basically I'm saying I'm tired of jQuery being dragged, and I'm not dropping it)

One of the centerpieces of my porchfest empire has been "the Hourtron", a drag and drop tool for putting bands on porches. I posted about its first prototype in 2015. Since then I'd rewritten it a few times.

I remembered a failed rewrite in 2019... I think one big mistake was avoiding resue of tragically unhip jQueryUI, which seems to have a particularly good drag and drop facility - I didn't search THAT hard for a lighter replacement this go 'round, but the ones I did try didn't have a simple "snap" facility.

Also I'm not sure if in 2019 if I was doing Declarative drag and drop "properly", but here I have the server endpoint (in PHP) return the entire state of band, porches, and the gigs linking them, and I remove the whole grid and build it up again in Vanilla JS every time a band is dropped (avoiding "batch processing" was THE major impetus for the rewrite; some porchfests now have porches scheduling their own bands, and i didn't want to risk overwriting that information. The other big win was the "untimed" column where you could show a band was associated with a porch (often at the band's request) while holding off on figuring just when the band would play.

Honestly I love the conciseness of old school jQuery in messing with the DOM; $(".someclass").css() or $("id") is so much more concise than the vanilla equivalents, making `map()` like behavior over all matching elements effortless, years before JS had .map() built in

and the drag and drop code was similarly concise, here are some bits and pieces for the flavor:

Setting up each band:

$band.draggable({
    snap:".target",
    snapMode: "inner",
    snapTolerance: 10,
    cursor:"move",
    start: function() {
        $(this).addClass('dragging');
    },
    stop: function() {
        $(this).removeClass('dragging');
    }
});

Making the drop targets:

$(".timeblock").droppable({hoverClass:'over',drop:bandDropOnTime});
$(".untimedblock").droppable({hoverClass:'over',drop:bandDropOnUntimed});
$(".unplacedblock").droppable({hoverClass:'over',drop:bandDropOnUnplaced});

And then getting the drop and the drag (using the jQuery paradigm of stuff things as ".data()" on the element.

function bandDropOnTime(event,ui){  
    const $timeblock = $(this);
    const porchid = $timeblock.data("porchid");
    const hour = $timeblock.data("hour");
    const minute = $timeblock.data("minute");
 
    const $bandblock = ui.draggable;
    const band = $bandblock.data("band");
    const bandid = $bandblock.data("bandid");

    saveGigData(bandid,porchid,hour,minute,band.performancelength,band.actname);
}


One gotcha with that was not using fat arrow notation; jQuery relies on a sense of "this" (via "$(this)") when attaching an event handler - but "this" has fallen from grace (and rightfully so, it was pretty confusing) and fat arrow tends to blow away - so oldschool anonymous "function()"

Another gotcha I had forgotten about: the draggable bands were much wider than the target time slots, and dropping would often not put the band where it looked like it had been let go. So the actual "draggable" part of the band element is the size of one time slot, and then it has a child that's as wide it needs to be to indicate the desired performance length. That seemed to be the simplest workaround for making it clear where the user was trying to drop the band (as you can see in the screen shot I use an extra shot of "red" for the start time- I started that as a diagnostic placeholder but actually highlighting the start time is a reasonable UX thing to do.)

tracking the field

 Software engineering job openings hit five-year low?

It's been a grind for so many developers. A thoughtful article about some of the possible causes of dot bomb 2.0.

Thursday, February 13, 2025

not the one from 2001

Sometimes I get a little concerned about the explosion in complexity around UI and software architecture in general. Interesting hot take from Martin Fowler here, that many successful miroservice architectures start up as a monolith, and then break parts out as the needs become more apparent.

Sunday, February 2, 2025

snip snip

6 CSS Snippets Every Front-End Developer Should Know In 2025. All good stuff, but sometimes I can't shake the feeling that animations and transitions were easier in JQuery - admittedly CSS is much more declertive but things like transitions can seem a lot more intuitive to write as a verb of script - stuff that happens - rather than a noun of styling, stuff that is.

Thursday, January 30, 2025

npm note

 At my new job we use veracode to scan for vulnerabilities, including npm packages that are children of packages we use.

those seem a little tricky... I think the options are:
1. tell veracode to ignore it IF its dev dependency/internal tool only
2. upgrade the parent package(s) and hope it pulls a more recent version
3. use { "overrides" : "foo":"1.0.0" } (new since 2021 NPM v8.3.0)

everything is a little fraught but that last one seems the most promising to me.

(via Stack Overflow What is the role of the package-lock.json? )

Tuesday, January 21, 2025

surveying eslint-disables across a code base

An architect was mentioning some counts of eslint-disables in our code base, and I wanted to take a closer look.

(I know different devs have different intuitions about the criticality of some of these rules: I know for me, if there's a trade off between "concise, more readable code that keeps attention to what's actually new" vs "well-enforced lint standards and rigorously enforced strong typing at every level that MIGHT help turn a runtime error into a buildtime problem", I lean towards the former.)

I started by recursive grepping for those lines, and putting them into a .txt file so future steps would be faster:

grep -r "eslint-disable" | grep -v node_modules/   >  ~/raw.txt

I then used CoPilot to come up with a node.js script to break up the lines and do counts by subproject in the monorepo:)

const fs = require('fs');
const readline = require('readline');

// Create a read stream for raw.txt
const fileStream = fs.createReadStream('raw.txt');

// Create an interface to read the file line by line
const rl = readline.createInterface({
  input: fileStream,
  crlfDelay: Infinity
});

// Initialize the counts and totalCounts objects
const counts = {};
const totalCounts = {};

// Process each line
rl.on('line', (line) => {
  // Split the line on whitespace
  const sections = line.split(/\s+/);

  const regex = /\.\/([^\/]+\/[^\/]+)\//;
  const match = sections[0].match(regex);

  if (!match) {
    console.log(`NO MATCH FOR ${line}\n`);
    return;
  }

  const appOrLib = match[1];

  const offset = sections.findIndex(section => section.startsWith('eslint-disable'));

  if (offset === -1) {
    console.log(`NO MATCH FOR ${line}\n`);
  } else {
    // Join the remaining parts on " ", ignoring "*/"
    const remainingParts = sections.slice(offset + 1).filter(part => part !== '*/');
    const joinedParts = remainingParts.join(' ');

    // Split each remaining part on "," and treat them separately
    const finalParts = joinedParts.split(',').map(part => part.trim());

    // Initialize the appOrLib object if it doesn't exist
    if (!counts[appOrLib]) {
      counts[appOrLib] = {};
      totalCounts[appOrLib] = 0;
    }

    finalParts.forEach(part => {
      const key = part || '[EMPTY]';
      if (!counts[appOrLib][key]) {
        counts[appOrLib][key] = 0;
      }
      counts[appOrLib][key]++;
      totalCounts[appOrLib]++;
    });
  }
});

// Print the counts object when done
rl.on('close', () => {
  for (const appOrLib in counts) {
    console.log(`${appOrLib}: ${totalCounts[appOrLib]}`);
    const sortedKeys = Object.keys(counts[appOrLib]).sort((a, b) => counts[appOrLib][b] - counts[appOrLib][a]);
    sortedKeys.forEach(key => {
      console.log(`\t${key}: ${counts[appOrLib][key]}`);
    });
  }
});

Thursday, January 2, 2025

thumbs way up

When you enjoy thinking about UI/UX, it's fun and instructive to see evolutions in products you use every day, and think about what their designers were trying to accomplish, and what you might think they could do better.

So in FB Messenger, they recently changed the "send message" button - I believe that previously it was inactive until you typed some message content. Now it's a "thumbs up" button when the message body is empty, which sends a big thumbs up (btw, isn't that a rude gesture in some cultures? I was just googling to see if FB uses anything different elsewhere...)

So I noticed this on accident, when I see I had sent a big thumbs up between 2 of my own messages... (I quote "The Tao of Programming": "A program should follow the 'Law of Least Astonishment'. What is this law? It is simply that the program should always respond to the user in the way that astonishes him least.")

So, on the one hand, I could see that an easy general "OK" response is potentially useful, and a little easier to get to here than the usual "reaction" picker for previous messages. On the other hand... it seems weird to just make that the default message - especially to ones own messages. When you accidentally send one against your own message, it seems weirdly insecure or self-aggrandizing.

I wonder if they considered changing the "just send a thumbs up" function to activate only after the other person has written something back? That seems like a more useful scenario. But I guess would make the UI even more chaotic and unpredictable.