HN user

invalidator

525 karma
Posts0
Comments120
View on HN
No posts found.

TDD is done in a tight loop (minutes) while coding. For every little micro-feature/fix, you write a test for the new behavior you want, implement the minimal ugly fix to get the test to pass, then rely on the tests so you don't regress as you clean up.

LLMs struggle with TDD. They want to generate a bunch of code and tests in large passes. You can instruct them to do red/green TDD, but the results aren't great.

SDD starts before implementation, and formalizes intent and high-level design. LLMs eat it up. The humans can easily reinvent the worst parts of waterfall if they're not careful.

They're not mutually exclusive.

You can do better: cut to 0%, let the needle fall, go back to 100% for a moment to catch the needle as it approaches the bottom of the scale. If you want to be fancy, add some quick sinusoidal ramps to smooth out the derivative.

The interest is BECAUSE it's well explored territory. The concept is proven and works fine.

On the low end where RISC-V currently lives, simplicity is a virtue.

On the high end, RISC isn't inherently bad; it just couldn't keep up on with the massive R&D investment on the x86 side. It can go fast if you sink some money into it like Apple, Qualcomm, etc have done with ARM.

As a kid I took a lot of classes at the Lawrence Hall of Science in Berkeley, which was paradise for fledgling nerds. On the last day they would have a little closing ceremony with some cute little science experiment. One of my favorites was "Going Out With A Bang".

The instructors would bring out a helium balloon and a candle on a meter stick. The balloon goes pop, huzzah.

Then the twist. "Hey, wanna do it again?" All the kids would be like "meh, I guess?" They would then bring out a balloon full of hydrogen (maybe some oxygen too?). It would look identical to the first one, floating there tethered to the lab bench.

When the candle hit the second one, it made a white flash and a really sharp BANG. It was an order of magnitude louder, and you could hear the transient bouncing off the walls and echoing in the halls. It made an impression.

If you need a specific version of one package: apt-get install hello=2.10-3

If you want to lock down versions on a system, Apt Pinning: https://wiki.debian.org/AptConfiguration#Using_pinning

If you have a herd of systems - prod environments, VMs for CI, lots of dev workstations, and especially if your product is an appliance VM: you might want to run your own apt mirror, creating known-good snapshots of your packages. I use https://www.aptly.info/

Containers can also be a great solution though.

MCAS autonomously adjusts trim downward. The trim switches override MCAS, but when released, MCAS can resume trimming down again. The trim adjustments don't "override" the pilot's elevator inputs (MCAS has no direct control over the elevators), but they can make the controls so heavy that it's impossible to pull up.

The BIOS was an abstraction layer. In the old days, not everything was 100% IBM PC compatible. There were lots of weird graphics cards. Some systems had incompatible disk and keyboard controllers.

There was no memory protection in Real Mode, so you could always poke the hardware yourself, but something written on a Tandy wasn't going to work on a Zenith unless you supported both, or ran everything through the BIOS.

Over time, the OS took over the HAL role, with the BIOS only being used until the OS could load native drivers. Now it's UEFI... same idea with a higher greater level of abstraction and modularity.

It's not the same.

For a simple example, let's say you are simply driving in a circle. The car wants to lean toward the outside. The linear motors can provide a countering force, lifting the outside, lowering the inside, so the car stays level. Variable damping can only control the rate that it rolls. It will still roll in sub-second timescales, unless it completely locks down the suspension, which is terrible for both handling and comfort.

For another simple example: going over a speed bump. Linear motors can lift the front wheels over the bump, and then the rear wheels, so the body stays level the whole time. An active damper can go full-soft the moment the wheel hits the bump, but the compressed spring will still start lifting the front of the car. An active damper can do a better job managing the rebound on the far side so it doesn't oscillate, but it can't entirely prevent the bump from pitching the body up and down in the first place.

That's not to say it's worthless. Very fast active dampers can improve both handling and comfort. It's just nowhere near the level which is possible with linear motors.

It depends on the language you're using.

For my Ruby example, each of those method calls will allocate an Array on the heap, where it will persist until all references are removed and the GC runs again. The extra overhead of the named reference is somewhere between Tiny and Zero, depending on your interpreter. No extra copies are made; it's just a reference.

In most compiled languages: the overhead is exactly zero. At runtime, nothing even knows it's called "data" unless you have debug symbols.

If these are going to be large arrays and you actually care about memory usage, you wouldn't write the code the way I did. You might use lazy enumerators, or just flatten it out into a simple procedure; either of those would process one line at a time, discarding all the intermediate results as it goes.

Also, "File.readlines(i).count" is an atrocity of wasted memory. If you care about efficiency at all, that's the first part to go. :)

The author keeps calling it "pipelining", but I think the right term is "method chaining".

Compare with a simple pipeline in bash:

  grep needle < haystack.txt | sed 's/foo/bar/g' | xargs wc -l
Each of those components executes in parallel, with the intermediate results streaming between them. You get a similar effect with coroutines.

Compare Ruby:

  data = File.readlines("haystack.txt")
    .map(&:strip)
    .grep(/needle/)
    .map { |i| i.gsub('foo', 'bar') }
    .map { |i| File.readlines(i).count }
In that case, each line is processed sequentially, with a complete array being created between each step. Nothing actually gets pipelined.

Despite being clean and readable, I don't tend to do it any more, because it's harder to debug. More often these days, I write things like this:

  data = File.readlines("haystack.txt")
  data = data.map(&:strip)
  data = data.grep(/needle/)
  data = data.map { |i| i.gsub('foo', 'bar') }
  data = data.map { |i| File.readlines(i).count }
It's ugly, but you know what? I can set a breakpoint anywhere and inspect the intermediate states without having to edit the script in prod. Sometimes ugly and boring is better.

I can't help but feel like a lighter weight VM (versus electron) is what we actually want. Or at least what _I_ want, something like UXN but just a little more fully featured.

That's basically the JVM, isn't it?

It's interesting to think how some of the reasons it sucked for desktop apps (performance, native UI) are also true of Electron. Maybe our expectations are just lower now.

TempleOS deserves a mention here. It boots up into a simple system with a C-like language featured front and center. It makes it easy to write small programs with simple graphics.

It feels like the Apple IIe and GW-BASIC systems that I cut my teeth on, but with more features (Simple windowing TUI, good editor, extensive help system, many examples) and high performance (compiled code running on modern x86 hardware).

I don't know if I'd give to a kid as their only computer (For better or worse, there's no networking, and I wish it had high res graphics modes and a little more polish), but it's a really cool environment for a curious young mind to explore.

https://templeos.org/

In my experience, farsighted people tend to be very casual with their glasses, whereas us nearsighted folks protect them carefully.

My theory is that being nearsighted, I can see my glasses - every smudge or speck of dirt is immediately visible to me because it's not too far from my focal distance. For farsighted people, the glasses on their face are so far out of focus that those things simply disappear.

If I lay down with my glasses and they rub against my face or the pillow, I know they're going to get smudged and I'll suffer eyestrain until I find a lens cloth and carefully clean them. Any nick in the lens will drive me crazy until I can get it replaced. As such, I am very protective of them - any time I lay down I'm fold them closed and lay them face-up on the headboard so they won't need more than quickly brushing off a few specks of dust. I also buy very thin, light frames because I can see them pretty clearly in my peripheral vision.

The people I've known who are farsighted (including my husband), are oblivious to anything on the glass. He'll just roll around in bed with them, and maybe once a week ask me to clean them for him. We have to get him heavier frames because the lightweight ones would be a mangled mess in a month.

So yeah, I'm guessing you have the latter experience... Sturdy frames and you aren't too bothered by some smudges when you get up.

For a couple decades now, the way I have interviewed people is to ask a simple, very-high-level question, then repeatedly asking either "So how does it do that?" (drill down), or "What happens next?" (back out).

For instance: What does 'printf("hello, world\n");' do? Obviously, it prints something, but how does it do that? Pretty quickly you're talking about includes, macros, libc, linking, machine code, system calls... One question can easily fill an entire interview slot.

The fun thing is there's no "right" answer. Nobody is expected to know everything about how software works, but everyone is expected to know something. This format gives the interviewee the opportunity to show off what they know best, and the interviewer gets to pry in to see how deeply they know it.

I'm a low-level guy so that's the direction I tend to probe. Usually someone else asks a similarly abstract high-level question. One of my favorites is: "Design a parking garage". Again, there's no right answer. It's a prompt for the candidate to show what they know. Very quickly they're coming up with functions and class hierarchies and/or data structures for vehicles, spaces, turnstiles, payment kiosks, figuring out how to pass them around, etc. The interviewer has plenty of opportunities to pry into design tradeoffs, add complications, and so on.

The grand idea is to have a deep conceptual discussion instead of just seeing if they can write a few lines of code. This also demonstrates how well they can communicate. The catch is you have to be sure they give some actual concrete answers in a few places, and aren't just fast talkers.

Safety regulations.

"A Car" needs to be able to operate safely on highways at 60+ MPH. That means: horsepower, braking, crumple zones, airbags, crash testing, etc. By the time you add those up you've already exceeded that budget, and you only have a glorified rickshaw. People want doors, roll-down windows, etc, which all add cost... they're going to be better served with a used car in that price range.

The closest things that exist are Neighborhood Electric Vehicles. They're basically golf carts with seatbelts, allowed to operate on city streets up to 25 MPH.

It would be great if the US had something like Japan's Kei Cars. Those are $10k and up, fast and safe enough for local highway use (say 50MPH)... It works in Japan because urban traffic tends to be low speed. They are less practical in the US where freeways run through every city, but they'd still work for many people. They would require significant regulation changes.

You can! Get an energy-monitoring smart plug[1]. Set it up on Home Assistant[2] or another smart-stuff system. Then create an Automation which sends an alert when it drops below 25 watts for 3 minutes.

It's all-local, and requires no integration with the washer. 220V dryers may be a little trickier, but still easy enough if you're comfortable with basic electrical stuff. You can put a sensor on an indicator light, or put a current clamp on one of the wires. Usually you only care when the washer is done though, so you can move your clothes over.

[1] I use https://www.amazon.com/dp/B0BPY5D1KC for Zigbee, but there are WiFi/Z-wave/other options.

[2] https://www.home-assistant.io/

Reducing framerate doesn't help much when there isn't a lot changing between frames. Here are some better optimizations:

Noise reduction, so you compress less useless noise: -vf hqdn3d

Turn up the Constant Rate Factor. This will make better visual tradeoffs than decreasing frame rate. 23 is a good starting point for h264, but keep increasing it until you don't like how the content looks: -crf 23

Throw more CPU at the problem: -preset veryslow

Classic parallel (ATA, PCI, parallel ports) is going away and not really coming back. The newer interfaces like 4xPCIe and USB are multiple serial interfaces bonded together.

The difference is all the bits in a true parallel interface operate synchronously. All the bits get set; the clock pulses to signal data is ready; all the bits update again. The problem with this approach is you have to wait for all the bits to settle before you can read. It's no problem at low frequencies, but when you get into hundreds of MHz or more, it becomes a real challenge to get everything time-aligned.

4xPCIe channels operate on 4 independent clocks. They have the same nominal rate, but they don't have to be precisely aligned. Each channel is transmitting a single frame at a time - it's not splitting them into 4-bit packets and sending 1 bit down each channel.

I'm hard-pressed to think of a new technology in the last decade that implements splitting a byte into bits and sending each down a separate line.