Wednesday, August 27, 2025

advice from a successful UX Engineer Job Seeker

 

It's not perfect but I'm thankful I found something so quickly. If you are currently unemployed or maybe find yourself unemployed soon here are some things that helped me find a new role so quickly:
1. You WILL be taking a pay cut. The market is flooded right now. Companies know this and are taking advantage. The faster you accept this the faster you can move on and apply for roles with a lower salary. The way I looked at it was taking a pay cut is still more than collecting unemployment.
2. You may have to go back into the office (at least a couple days a week). I was very very lucky to find another completely remote role. This was NOT the norm out there. Every other company I was working with was hybrid, some of those were in Boston. I was prepared to do it but thankful I don't have to.
3. Look outside your role. Again I'm super lucky I found something directly within my current niche. I'll even be working with a design system but I was entertaining taking a QA role which is related but not a direct match to what I used to do.
4. You need to be sending at least 10-20 applications daily. Many of these jobs I don't believe were even real because I would get a rejection email within minutes of applying. I think some places are just collecting a pool of candidates. But you don't know which ones are real and which aren't.
5. Branch out from LinkedIn. I did use LinkedIn but I also used builtin, indeed and dice. I also looked up practically everyone I ever worked with to see where they were and if their company was hiring.
6. Lastly remind yourself it's not forever. I took a less than perfect role, but I don't have to stay here forever. The economy will get better and things will eventually go back to normal.

Thursday, August 21, 2025

(Misuse of) Javascript broke the web

Wow - JavaScript broke the web (and called it progress).

What a well-written, thoughtful analysis of things that have been making my developer life less fun for a decade. 

Some quotes:

The tragedy is, none of this is necessary. Once upon a time, we had a fast, stable, resilient web. But we replaced it with a JavaScript cargo cult.

Now it takes four engineers, three frameworks, and a CI/CD pipeline just to change a heading. It’s inordinately complex to simply publish a webpage.

[...]

Around 2010, something shifted. The iPhone was ascendant. Native apps were sleek, fast, fluid. Angular, the first mainstream JavaScript framework for the web, had just emerged. And suddenly, every CMO started saying the same thing in website briefs: “Can we make it feel like an app?”

Developers, armed with new frameworks and good intentions, said yes. JavaScript could, in theory, do seamless transitions and slick UI. So we built towards that promise. Or tried to.

Spoiler alert: we didn’t get app-like performance. We didn’t get better user experiences. What we got was an arms race of complexity.

[...] 

 And so, we rewrote the rules of web development around entirely different needs, almost overnight. Not content, not speed, not interoperability, not discoverability. Just code.

[...]

Fast forward to today. Ironically, after years of chasing abstraction and complexity, the JavaScript ecosystem is now scrambling to reintroduce the things we lost.

Server-side rendering? Back in fashion. Routing? Carefully managed. URL structures, metadata, even HTML output – all being rebuilt, one plugin at a time.

 If I disagreed with anything, it's this:

Today, we optimise for “DX” – developer experience. Not user experience. Not performance. Not outcomes.
 

For a certain subset of developers... not true. At all. It's a mess and everything is harder than it seems like it should be - complexity is not counted as a cost and "ability to follow it in your head" is not prioritized at all. 

If you are old enough to remember the old 8bit home computers - they used BASIC even though it was slow and interpreted - on machines that were already slow - because the run-eval loops is SO powerful. And for me, there's a through line to that technology, some early scripting in like Perl and what not, and the Evergreen/Buildless stack I prefer now - everything is transparent, everything shows its work. Ever since the rise of Angular my day job has been about building blackboxes, as if everything was a standalone binary app. 

Wednesday, August 13, 2025

copy and paste images right into messages...

 I had to switch to a Windows machine at work. Its default screenshot-maker is launched via win-shift-s, and it copies the shot to the clipboard. What I was surprised to learn is how many communication programs now support "copy and paste an image into a message" - I thought maybe it was a Teams flex, but Gmail and FB Messenger also support it.

It's interesting because, programming wise, that's a pretty complicated thing to support. I think they are quietly taking the raw/base64 data, quickly uploading it, getting a file reference, and then putting a tag with the image in the WYSIWYG content.

I have to put it on my Startling Technology list - stuff that really impressed me when it came out, like Google Maps slidey tiles, youtube letting anyone upload video, ChatGPT+Dall-E... not quite as exciting as those but still.)

Monday, August 4, 2025

Upsert

 Today I learned a new-to-me term "Upsert", which is a lot nice than the "createOrUpdate" compound I might have used previously.

Friday, July 25, 2025

Thinking about Agile and the Games Industry

 Something I wrote into Teams at work as we're discussing our Agile process:

I was listening to the gaming podcast "Get Played" the other day - very idiosyncratic episode where one of the 3 improv comedian hosts [Nick Wiger] was running it solo. He had some experience doing tech support then QA then some design for Activision (he was on the team for the old Fantastic Four movie tie in game). Anyway, it reminded me of how much the game industry was responsible for popularizing Agile - both the scale of what a AAA title takes, and the process of nightly builds, and constant deliveries. 

 I mean, I'm not sure I love game industry as a model; they are in forever crunch mode and really exploit the fact that so many young smart people will sacrifice so much to be In The Industry. But for those among us who dig big games, it's interesting to think about the tie-in with how Agile structures our working day lives.

Gall's Law

 Gall's Law: A complex system that works is invariably found to have evolved from a simple system that worked.
The corollary is devastating: A complex system designed from scratch never works and cannot be patched up to make it work.
This is why:
- Microservices migrations often fail when done as big-bang rewrites
- New frameworks struggle against battle-tested ones
- "Clean slate" projects often become technical debt faster
Start simple. Add complexity only when the simple system breaks.

Robert Roskam


Thursday, July 24, 2025

final answers for export ReactFlow as PDF and exporting as one step


 I mentioned earlier all the heartache we had trying to export ReactFlow diagrams via KendoPDF (short of it was KendoPDF assumed the visual descriptor information for a SVG would be embedded, not external CSS - see this github issue for Kendo after ReactFlow helped me figure out it wasn't going to be solved as a ReactFlow thing.

After investigating other possible paths (ReactFlow talks about doing server side stuff via Puppeteer, and react-pdf was another package I might have started looking at. (We also considered the caveman approach of just firing the browser print dialog and letting the user save as PDF - but even that had terrible scaling problems and wasn't WYSIWYG)) we combined html-to-image package (which is the official recommendation - but be sure to heed the advice of locking in the version to 1.11.11!) with jsPDF.  (this meant we had to glue the diagram as a PNG rather than a scalable SVG - jsPDF doesn't handle SVG, maybe for reasons related to KendoPDF's not handling it. SVG is a tricky beast to render, historically..)

One gotcha was this... we had a requirement of wrapping the diagram in a standardized title frame for the PDF - but that frame wasn't there during normal use. With KendoPDF we had a rather neat solution: it added a "k-pdf-export" class, and we could use that to show a frame that was usually hidden but would then show up on export.

With the new solution, it wasn't clear when we could add in that class. My first pass was to do a "preview / export  mode" but that wasn't really part of the desired UX... they just wanted the class added on the fly, but that gets tricky with React and DOM rendering cycles. (generating and downloading a snapshot is pretty imperative, so add a class, snapshot, remove class got weird.) 

In short, doing a

    await new Promise(requestAnimationFrame);

after each mutation of the classList for the wrapping object seemed to cover it 

Wednesday, July 23, 2025

hot take

 Way Too Hot Take: TypeScript is the Hungarian Notation of our time. 

Monday, July 21, 2025

commented out code: make a PDF

One of my least favorite lint-ish ideas is that you shouldn't have commented out code, that it's ok to hope future coders know there's useful code to be found in the repository, if they don't mind foraging like hungry raccoons through trash.

BUT, that's the way my work seems to be leaning.

Anyway, some code I am taking out but would like to have handy:

  // const a = document.createElement('a');
  // a.setAttribute('download', 'reactflow.svg');
  // console.log(dataUrl);
  // a.setAttribute('href', dataUrl);
  // a.click();

that's just a handy of making a link to trigger a download, and even after we decided to pass dataUrl to jsPdf, I could see cases where we'd want to download the graphic file directly (for instance, the option to download an SVG, when the PDFs we make can only embed PNGs, which lose the nice formatting.)

Thursday, July 17, 2025

KendoPDF and SVGs that depend on CSS

 I made some progress from the "why won't ReactFlow edges show in KendoPDF exported PDFs" I had the other day, at least in understanding the problem.

I started this thread on the reactflow github that got me digging into - A ok, Kendo DOES do SVGs, right? So what's special about these node lines?

Eventually it got me back to starting this bug report on the Kendo side - along with this crude StackBlitz (I have to remember StackBlitz, it's one of the better CodeSandbox type things I've seen, love being able to tool around in a lil terminal.

My experiments indicate that ReactFlow depends on CSS to do stuff like stroke and strokeWidth inside of PNGs but that gets ignored by the KendoPDF rendering process. 

Bummer! I need to see if html2canvas or html-to-image have better results, maybe combine with jsPdf. 

Tuesday, July 15, 2025

problems with kendopdf and reactflow

 It felt like Kendo-React-PDF might be an easy way of exporting stuff made in ReactFlow but weirdly the edges (lines connecting the nodes) don't show. I started with a minimal bit of hello, world for both to show that it wasn't something else that was causing the lines not to show:

 run it with

npm init vite my-react-flow-app -- --template react

cd my-react-flow-app

npm install @xyflow/react

npm install @progress/kendo-react-pdf

npm run dev


App.jsx:

import { useState, useCallback, useRef } from "react";
import {
  ReactFlow,
  applyNodeChanges,
  applyEdgeChanges,
  addEdge,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { PDFExport } from "@progress/kendo-react-pdf";
 
const initialNodes = [
  { id: "n1", position: { x: 0, y: 0 }, data: { label: "Node 1" } },
  { id: "n2", position: { x: 0, y: 100 }, data: { label: "Node 2" } },
];
const initialEdges = [{ id: "n1-n2", source: "n1", target: "n2" }];
 
export default function App() {
  const [nodes, setNodes] = useState(initialNodes);
  const [edges, setEdges] = useState(initialEdges);
 
  const pdfExportRef = useRef(null);
 
  const onNodesChange = useCallback(
    (changes) =>
      setNodes((nodesSnapshot) => applyNodeChanges(changes, nodesSnapshot)),
    []
  );
  const onEdgesChange = useCallback(
    (changes) =>
      setEdges((edgesSnapshot) => applyEdgeChanges(changes, edgesSnapshot)),
    []
  );
  const onConnect = useCallback(
    (params) => setEdges((edgesSnapshot) => addEdge(params, edgesSnapshot)),
    []
  );
 
  const exportToPDF = () => {
    if (pdfExportRef.current) {
      pdfExportRef.current.save();
    }
  };
 
  return (
    <div>
      <PDFExport ref={pdfExportRef} fileName={"output.pdf"}>
        <div style={{ width: "100vw", height: "80vh" }}>
          <ReactFlow
            nodes={nodes}
            edges={edges}
            onNodesChange={onNodesChange}
            onEdgesChange={onEdgesChange}
            onConnect={onConnect}
            fitView
          />
        </div>
        <button onClick={exportToPDF}>click me</button>
      </PDFExport>
    </div>
  );

Friday, July 11, 2025

vibe coders know the value of everything and the cost of nothing.

I suspect that a big part of the next phase of dev work in general (like industry wide) is going to be more QA-like. 
 

A gray lining to the silver cloud of AI buddies (from the company and project perspective - obviously they are a threatening as hell frenemy for anyone who develops software for a living) - their code is so verbose. Beside the "tell" of explanatory comments after someone has used AI, some LLMs style favor a more baroque style. (This is anecdotal; it might be possible to direct an AI to be more concise, and maybe different LLMs act differently, this is just some empirical observation.)
 

I already think the industry doesn't put enough emphasis on simplicity and traceability and transparency, and there are places in my work's own code base where I think... this could be/should be more concise, and that enacts a long term cost in terms of maintainability. 


It reminds me of the old line "LISP programmers know the value of everything and the cost of nothing."...  and the old Microsoft vs Unix approach paradigms, where Microsoft would often do so much for you, but when it came time to understand it to fix some low level thing, it was rough going.  

Friday, July 4, 2025

chatGPT to make a simple score keeper

There's a pretty cool, kid-friendly party game called Without Fail - and even though the scoring is pretty simple (if you and your high bid partner complete a challenge, you get a point, if you fail you lost a point and everyone else gains a point) it can be fiddly to keep track of. So I had ChatGPT help me build a simple score tracker for it.

I feel like I'm a little ahead of the curve with my VPS, I can just throw this up and share it. I mean it could work as a static file on any personal site, but when I can just slap up server side stuff as well, I am really empowered with a real synergy with what ChatGPT can help out with.

Tuesday, July 1, 2025

Learning GraphQL

I am looking forward to tackling How to GraphQL.

I am not sure it always pulls its weight complexity wise (it seems like there would be ways to tell a REST endpoint to only send certain fields?) and depending on how your place handles security and authorizing specific calls you might not get the flexibility benefits it offers. 

 So something I should know more deeply, but have to overcome my skepticism... 

Wednesday, June 11, 2025

the internet's one weird trick

 One could criticize no-code for not offering the flexibility and nuance you can get by writing your own code, line by line. But the truth is, for all the hoopla about Silicon Valley's innovative genius, a huge number of apps don't do much more than awfully simple things. Seriously: Silicon Valley's main trick is just shoving things into a database and pulling them out again. I'm exaggerating, but only a bit.

 
I think of this quote so often, but was bad at googling about it.

But as frontend gets more and more complicated it seems more and more silly. Still the same stuff but in much harder ways.

Sunday, June 8, 2025

false simplicity

I probably spend too much time thinking about my "unpopular opinions" in coding, looking for like-minded souls who might agree with me that the industry has emphasized some of the wrong thing over the past decade or two, and trying to come up with arguments to persuade smart coders who disagree with me that even where I don't share their intuitions, I understand both sides enough not to be too much of a pain in the butt curmudgeon about it.

Really I might be better off shutting up, getting to work, and pretending that TypeScript is obviously The Right Thing, and OF COURSE testing every line of code as a unit, in a reductionist context free way, carries its weight. I'm well into the back 9 of my career, my best hope is to fend off AI bots (or rather learn to leverage them but hope they don't suddenly become good at big picture, senior level coding rather than extremely helpful junior level bots that are also good at remembering documentation) long enough to stumble into retirement without TOO much a salary collapse. Maybe I should save my unpopular views, no matter how thoroughly rationalized, for my side projects where no one will care if I'm just a overgrown-script-kiddy lone-gun-cowboy/caveman.  

 So, that disclaimer aside (though there's always disclaimer...)

The number one thing the web UI industry has lost is appreciating simplicity and transparency for its own sake. For a lot of challenges they faced, the answer was get a shiny new toolkit, add in a layer, learn a new specialized syntax. (A reverse of "NIH", not invented here - the impulse is "lets find a toolkit to do this for us rather than trusting our own code") There is sometimes a false simplicity in these tools, a veneer of "use our complex engine humming under this glossy metal hood" - but god help you if the engine goes wrong (or you lazily didn't read QUITE enough of the docs to use it right) and you now have to figure out why its way of solving your problem PLUS a hundred other company's problems isn't going sour and no longer solving your problem.

(At the risk of frustrating one coworker who I might be brave enough to show this to - Tailwind for CSS is one example of a false simplicity for me. I mean, at one level it starts off obviously more complex, needing a build system vs the old day of "use any editor, save, reload in browser" ... and then requiring learning a new set of (admittedly crisper) tokens for CSS properties I already have to be fluent in. But I want to keep talking with this coworker and try to "get" the appeal of it more, I think he sees something in it I should lean into more.)

But I trust enough of my fellow devs will at least acknowledge the idea of simplicity and transparency enough that they'll understand some gripes I got stuck in my head last night.

Lately I've run into some lint rules or code smell guidelines that seem to deprecate comments - I guess the idea is code should be self-documenting so most explanatory comments are a sign things are awry. And then I get hit by lint rules forbidding commented-out code. I mean yeah, I guess grepping through files could be confusing if a TON of your code was comments, but often commented out code is just helpful signposts of where you've been - and where you might want to go again! Comments are right there at hand in proximity to the code in a way commit messages and PR comments aren't, and while you should always debug the code not the comments, I don't quite see why people don't trust comments more.

Another anti-simplicity aspect is over-zealous unit testing. Most unit tests either fall into: you're writing the same functionality twice (i.e. testing for something simple that's like, just right there in the code), or you're testing your mocks more than you're testing you're code. Or you're describing your contract with the outside world but more likely than not describing your implementation, not your higher level result.

And like yeah, TypeScript. TypeScript is an awkward at best syntax, and I think makes it harder to get the gist of code, even as it does better at empowering autocompletion in editor. And I feel like lately I've really been suffering through "oh you said this can be a string or undefined, NOT string or null". I'm not going to live long enough to what to ponder the "correct nature of nothingness" that deeply... but anyway. TypeScript feels less agile. You can't just change some code (and some of the code calling it) you have to change at least one other place to describe it. Like to be there's just this weird seeming contradiction with people who on the one hand seem to hate comments, and want the code to speak for itself, but also don't want the code to speak for itself, they want to make sure everything is in a formalized type. 

Anyway. Just my ranting for today. I don't know how much I want to publicize this article, lest I be seen as a old fart pain in the ass about where modern software has gone. But I try to reach out to my fellow "hey things have gotten out of hand. we're not solving that many harder problems than we were 20 years ago, we just have more complex ways of doing so"...

Wednesday, May 28, 2025

feeling loopy

Today at work I was solving two infinite loops, both based on having to finesse dependency arrays in useEvent() or useMemo() type things in React.

Hopefully this is less of an unpopular opinion than it was a while back (I think the industry as a whole is more aware of React's limitations) but React's flavor of declarative programming is so damn "jumpy" compared to, say, the jQuery stuff that preceded it. jQuery coding could suck if you weren't sure what was modifying the DOM, but in general the DOM was stable and the methods to change it (often after doing server side rendering, another technology people had to reinvent for some reason) were more or less transparent.

(And hell... when you have React controlling an <input> tag by using state? So that every thing the user types in changes the state, and then React sets the value tag? The ONLY reason the cursor doesn't jump to a default position is because of React's optimizations to recognize it doesn't actually need to rerender that text box. It seems crazy to me that a fundamental UX experience of being able to type anywhere in an input field is dependent on an optimization like that, since cursor position is some kind of hand-wave-y implied state.)

But now with React... like we used to have MVC, Model-View-Controller as a no frills way of doing UI. But React has always mashed together the Controller into the View. And when it's hard to realize you're modifying the Model, aka the state, or accidentally looking at the wrong state to trigger more actions, man, that's just a recipe for infinite loops and rendering that never ends.

Maybe some year the industry will recognize the power of simplicity. Or not, and we'll just hope the AI bots are doing a good job of thinking through the edge cases.

Tuesday, May 27, 2025

unix quotes

 I like this page of Unix quotes especially 

Contrary to popular belief, Unix is user friendly. It just happens to be very selective about who it decides to make friends with.   -- unknown

 and

UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity.   -- Dennis Ritchie

but most importantly 

UNIX was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things.   -- unknown (generally attributed to a Doug Gwyn, but no information found about him) 

 


Tuesday, May 20, 2025

thoughts on AI and autocomplete

Heh. Over the weekend I was thinking about that "TypeScript Origins: The Documentary" documentary a coworker linked.

I've only watched the first part one of the big points was the the in-editor/developer experience - especially stuff like autocomplete etc being much more robust when you have everything typed, and some of the devs complained about wasting half day chunks so much time with typos etc. (Which to me seems a little exaggerated maybe? Like if I have my tight iterative code-run loop going, if I don't immediately notice that I typo'd a field, something is probably really wrong.)

 

But it does make me think about the experience of code editors in general. One thing that shocks me is how popular autocomplete of quotes, braces, parens, and tags has become - like it seems weird to me that that's the default, and people are ok with it - for me the extremely minor convenience of saving a keystroke is vastly outweighed by the unpredictability of it all... I type code like syntax in a number of environments, and some complete and some don't, and while I'm barely a touch typist I really do like the idea that "what you type is what you get".

 

And now with AI being thrown into VS Code, my wide ranging ambivalence grows. Like sometimes it's hella helpful and the ChatGPT hints are just the right thing. But man... visually it's a mess. Sometimes it's literally difficult to see, is this code that's there, or is this code proposed by AI. (And a few times the AI - with its "this sounds very likely!" mentality - is actually fighting with the "old school autocomplete" that TypeScript was designed to support... I remember one humble refactor of some relative imports that was made maddeningly difficult as the AI tried to override what the older version of the editor could have done automagically) It's like you're constantly dealing with a pile of AI hallucinations...

 

This is why sometimes I still like sandboxing AI, but then I'm worried I might be a little left behind, that these systems with more collaboration and proactive file manipulation by LLM might be the neartime future

Thursday, May 15, 2025

the not-so-hidden costs of "simplicity and elegance"

 (This one for my fellow UI tech heads. Especially folks who cut their teeth on earlier eras of tech.)

Video: React is imploding, and that's a good thing...

My favorite comment comes early:

"But with the latest advancements, I was feeling like maybe I’m too old, too busy or maybe just too dumb to use this new modern flavor of React. And, by the looks of it, this feeling was shared by many other developers."

It goes on to explore how weird it is that Dan Abramov uses a different framework, Astro, to explain how React Server Components are doing their division of server vs client activity (as the video says "Dan is a core team member of the React team, and he spends most of his time explaining and shaping the mental models around React. Having a designated person in charge of  “explaining the mental models” behind a UI library that paints buttons on the screen should already be a red flag")

Besides the horrendous push to vendor lock-in, RSC's agenda seem bizarre - to paraphrase Ben Franklin it pays too much for the whistle of potentially having the same code bits reused back end and front (especially since because the serverside stuff can't use hooks? like what non-trivial component of the past 5 years would have avoided using a hook for managing its state?) Muddling the two codebase sides seems weird. (Hell, I feel like my ancient preferred evergreen/buildless stack of PHP plus Vanilla JS shares Astro's concise division, and that both offer a cleaner, more honest approach than Vercel's stepchild)

All this is part of a wider industry trend towards ignoring complexity as a significant developer cost multiplier. Often we are sold paradigms that sound simpler in the abstract but then the implementation ends up with huge complexity costs. Module Federation seems like a great way to break up a monolith and let separate teams cook, but WOW is its mix of "every client piece has its own server" rough to untangle when things go wrong. TypeScript - yes, there are many benefits of not having a morass of unclear JSON-y jumbles, but man the syntax is HARSH to read. (Maybe I'm biased but I think Java/C's way of showing primitive types was a world better - "int foo" vs foo:int where the meaning of any ":" character is utterly context dependent...)

(Those are two hot takes and potentially unpopular opinions so don't assume I CAN'T use these when called upon, I just think they have questionable cost/benefit ratios)

Sometimes it feels like a huge pile of "lets ensure job security" but if you've been in the industry for decades, it can really take its toll. UI is still addressing the fundamental problem of "how do we get information on and off some centralized computer" that we were 20 years ago, but the techniques now tend to be wolves in sheep's clothing of complexified 'simplicity', not to mention how we've eaten so much "flavor of the month" we're all feeling a bit sick.

Friday, May 9, 2025

smattering of applause for teams

A moment to think about the UX of tools used by (in my case) those making UX.

I think most devs who have used both Slack and Teams would prefer Slack. I've griped about Teams before - the "Teams" part of it feels more like shared email threads or blog-posts-with-comments than dynamic conversation. 

A recent update to Teams helped mediate that a bit; now a single "Chat" sidebar (which is where a lot of devs will live) includes Teams and channels, and both chats and teams posts can appear under a single unified "favorites" section.

I think there's still a conceptual problem where a persistent sense of "group chat" doesn't exist in Teams as an entity; while in theory a team could invite the right people to a group chat and rename that group, in practice I find most teams doing Agile gravitate to using the chat attached to the daily standup for intra-day chat, since there's a strong correlation with who needs to be there and who is in standup. It's simpler than having people have to think about which chat to use, or in which an earlier message is likely to be  found.

Tuesday, April 8, 2025

if you see something (build finished) say something

 Sometimes our build process take a minute, so I had Co-Pilot help build me a script to use the mac's text to speech to announce when it looks like we were ready to go:

#!/bin/bash

# Check if at least one argument is provided
if [ $# -lt 1 ]; then
  echo "Usage: $0 <nx_command> [args...]"
  exit 1
fi

# Build the nx command from the provided arguments
nx_command="$@"
log_file="nx_command.log"

# Run the command in the background and capture its PID
$nx_command &> $log_file &
pid=$!

# Monitor the log file for the specific line
tail -f $log_file | while read line; do
  echo "$line"
  if [[ "$line" == *"All remotes started"* ]]; then
    say "ready"
    break
  fi
done

# Wait for the nx command to finish
wait $pid


Tuesday, April 1, 2025

print design in browser via PHP

 It's funny in how so many ways I can use a little coding mojo and not have to fiddle with Word/Google Doc, Excel/Sheets - or Wordpress for that matter, but that's a longer story.

But yeah, helping my sweetie run together some tiny bits of campaign material via PHP:

<!DOCTYPE html>
<html>
<head>
  <title>Lynette Table</title>
  <style>
  body { font-family: sans-serif; }
    table { border-collapse: collapse;  font-size:1.5em;}
    td { border: 0px solid black; padding: 10px; text-align: center;
    
    padding:24px;
    
    }
    
    .bigger{
        font-size:1.4em;   
    }
  </style>
</head>
<body>

<table>
<?php
for ($row = 0; $row < 5; $row++) {
    echo "<tr>";
    for ($col = 0; $col < 3; $col++) {
                echo <<<HTML
<td>

Vote this Sat April 5!<br>
<b class="bigger">Lynette Martyn</b> <br>
Town Meeting Member, Precinct 12<br>
A progressive voice for your neighborhood<br>
<b>www.LynetteMartyn.com</b>


</td>
HTML;
    }
    echo "</tr>";
}
?>
</table>

</body>
</html>

 slapped on this webpage gave me



and that was a lot easier to iterate on and tweak than if I had to copy and paste each time...

Monday, March 31, 2025

on not upgrading

Tim Boudreau:

About the whole DOGE-will-rewrite Social Security's COBOL code in some new language thing, since this is a subject I have a whole lot of expertise in, a few anecdotes and thoughts.

Some time in the early 2000s I was doing some work with the real-time Java team at Sun, and there was a huge defense contractor with a peculiar query: Could we document how much memory an instance of every object type in the JDK uses? And could we guarantee that that number would never change, and definitely never grow, in any future Java version?

I remember discussing this with a few colleagues in a pub after work, and talking it through, and we all arrived at the conclusion that the only appropriate answer to this question as "Hell no." and that it was actually kind of idiotic.

Say you've written the code, in Java 5 or whatever, that launches nuclear missiles. You've tested it thoroughly, it's been reviewed six ways to Sunday because you do that with code like this (or you really, really, really should). It launches missiles and it works.

A new version of Java comes out. Do you upgrade? No, of course you don't upgrade. It works. Upgrading buys you nothing but risk. Why on earth would you? Because you could blow up the world 10 milliseconds sooner after someone pushes the button?

It launches fucking missiles. Of COURSE you don't do that.

There is zero reason to ever do that, and to anyone managing such a project who's a grownup, that's obvious. You don't fuck with things that work just to be one of the cool kids. Especially not when the thing that works is life-or-death (well, in this case, just death).

Another case: In the mid 2000s I trained some developers at Boeing. They had all this Fortran materials analysis code from the 70s - really fussy stuff, so you could do calculations like, if you have a sheet of composite material that is 2mm of this grade of aluminum bonded to that variety of fiberglass with this type of resin, and you drill a 1/2" hole in it, what is the effect on the strength of that airplane wing part when this amount of torque is applied at this angle. Really fussy, hard-to-do but when-it's-right-it's-right-forever stuff.

They were taking a very sane, smart approach to it: Leave the Fortran code as-is - it works, don't fuck with it - just build a nice, friendly graphical UI in Java on top of it that *calls* the code as-is.

We are used to broken software. The public has been trained to expect low quality as a fact of life - and the industry is rife with "agile" methodologies *designed* to churn out crappy software, because crappy guarantees a permanent ongoing revenue stream. It's an article of faith that everything is buggy (and if it isn't, we've got a process or two to sell you that will make it that way).

It's ironic. Every other form of engineering involves moving parts and things that wear and decay and break. Software has no moving parts. Done well, it should need *vastly* less maintenance than your car or the bridges it drives on. Software can actually be *finished* - it is heresy to say it, but given a well-defined problem, it is possible to actually *solve* it and move on, and not need to babysit or revisit it. In fact, most of our modern technological world is possible because of such solved problems. But we're trained to ignore that.

Yeah, COBOL is really long-in-the-tooth, and few people on earth want to code in it. But they have a working system with decades invested in addressing bugs and corner-cases.

Rewriting stuff - especially things that are life-and-death - in a fit of pique, or because of an emotional reaction to the technology used, or because you want to use the toys all the cool kids use - is idiotic. It's immaturity on display to the world.

Doing it with AI that's going to read COBOL code and churn something out in another language - so now you have code no human has read, written and understands - is simply insane. And the best software translators plus AI out there, is going to get things wrong - grievously wrong. And the odds of anyone figuring out what or where before it leads to disaster are low, never mind tracing that back to the original code and figuring out what that was supposed to do.

They probably should find their way off COBOL simply because people who know it and want to endure using it are hard to find and expensive. But you do that gradually, walling off parts of the system that work already and calling them from your language-du-jour, not building any new parts of the system in COBOL, and when you do need to make a change in one of those walled off sections, you migrate just that part.

We're basically talking about something like replacing the engine of a plane while it's flying. Now, do you do that a part-at-a-time with the ability to put back any piece where the new version fails? Or does it sound like a fine idea to vaporize the existing engine and beam in an object which a next-word-prediction software *says* is a contraption that does all the things the old engine did, and hope you don't crash?

The people involved in this have ZERO technical judgement.

Now Social Security isn't as life and death as nuclear missiles, and the challenge in getting people who can make any changes with COBOL is real. But I have zero confidence the DOGE bros are the ones to get it anywhere near right.

Friday, March 21, 2025

AgGrid note

 Just a quick note, AgGrid is pretty serious about its breaking changes in 33. Like if it detects the old ag-grid.css, it aggressively unstyles itself and you can't use the new theming.

"If I press these keys in the right order, I can do anything!"

 At work, Corey G mentioned how sometimes he'll do a demo for a client and make sure EVERYTHING is a single file (like include Base64'ig images if need be) That vibe reminded me of how I like "everything in one file" for p5.js, and how it harkens back to the old days of computer magazine type-in programs, and this comic.



windows taskbars through the ages

I switched to Mac about a decade ago. For a while I thought Windows' "one task bar item per window" was smarter than Mac's "one Dock item per application". Now I'm not sure (and I noticed Windows has drifted to Mac's approach) but I do know I tend to have too many windows open.


Wednesday, March 19, 2025

Sunday, March 16, 2025

The Palm PDA Circa 2000

Around 2000, my friend Tom Much wrote an article about Day in the Life of the Palm User. Amazing how useful PDAs could be, even before they were online. (Smartphones really are the spiritual successor of these things.)


 

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.