More from Bryan Braun - Blog
Oscar, an open-source contributor agent architecture - Oscar is a project from the Go community that aims to develop AI agents that assist open source maintainers. I think this is a great idea. Open source is a load-bearing pillar in our modern digital infrastructure and maintainers need help. If AI agents were able to reduce the maintenance burden, it could reduce burnout, improve project longevity, and encourage new development. Excited to see where this goes. Move on to ESM-only - A proper update on JavaScript’s move-to-ESM fiasco. I was pleasantly surprised to learn that tools like Vite are helping push the community migration forward. One clear takeaway is that new packages should publish in ESM-only (no dual-publishing of ESM + CommonJS). Last week, I ended up republishing Checkboxland as ESM-only, in large part due to the influence of this post. My keyboard ergonomics journey as an engineer - A nice write-up on keyboard ergonomics from my former co-worker Grant. This post inspired me to experiment with keyboards, and I’ve been using a split keyboard for the past two months now. Thanks Grant! You Must Read at Least One Book To Ride - The basic message here is that there’s an astounding amount of mediocrity in our industry (all industries, really), and all it takes is reading one book in a relevant topic for your work to make you stand out. Compelling, if true! Radiant - A technical deep dive into a startup that is working to replace diesel generators with portable nuclear reactors the size of a shipping container. As I read, I found myself carried away by the narrative and detailed explanations of the problem space and potential solutions. The article comes from tech investor Packy McCormick’s Substack blog, which explains the techno-optimism. Honestly, I’m here for it. He’s writing about people pushing the frontiers in fields that actually matter like energy, transportation, and biomedicine. These are things that will help people live healthier lives with fewer costs and more personal freedoms. Good reading, if you need that shot of optimism from time to time. Nobody Cares - A proper rant/observation about how so many things in the world could be better if people cared more. It got me thinking about the times when I did my best work and the other times when I didn’t. My working theory is that it’s not that people don’t care… it’s more that they don’t care about the same things. The streetlight installer in his post cares more about driver experience than pedestrian experience. The bureaucrat cares more about getting home by 5 to make dinner for their sick spouse, than working late to push for alternative bike ramp designs. What makes Japan “nice” isn’t that they care more… it’s that they care about the same things more. Seems plausible, right? Precocious Young People Should Do Deep Technical Training - “Science and technology drive the modern world. If you understand how they work, you can become a much more active participant in the world, instead of being at the mercy of what is effectively magic.” Of all the things I learned in the years I spent studying mechanical engineering, the most important was that I can understand anything… it’s just a matter of desire and time. I don’t know if that realization ever comes unless you’ve battled your way to an understanding of at least a couple highly technical subjects. The Colors Of Her Coat - This post left me awestruck. Through a series of stories, Scott Alexander makes a case that the long march of human advancement is methodically removing our opportunities to experience wonder. Modern humans no longer feel ecstasy when they drink a spiced beverage, hear an opera singer, or see an AI-generated portrait, because these wonders are no longer scarce and scarcity is what gives things value. So what happens when we finally arrive at our post-scarcity utopia (whether that be via earthly technology or spiritual afterlife)? Are we in heaven or hell? It’s a fascinating discussion, full of examples, poetry, and religious symbolism. LightNote - Interactive music theory lessons in the browser. Try the free sample… it’s pretty fun. I’m kinda jealous that I didn’t build this.
I write a lot of JavaScript but circular dependencies have always been kind of a mystery to me. The error messages always seem random and inscrutable, and sometimes there’s no error message at all! I wanted to understand this topic better, so I ran a series of experiments and wanted to share what I learned. Let’s break down circular dependencies in JavaScript. What are circular dependencies? Circular dependencies happen when your JavaScript import statements result in a loop: The loop can consist of two files, three files, or more. Any time your import statements create a loop like this, there’s a risk that your code won’t work as expected. How do you know when you have circular dependencies? There’s no easy way built into the language! In JavaScript, a circular dependency often manifests as a seemingly unrelated error (like a ReferenceError or TypeError). This is different from many other programming languages, which often tell you directly that your imports are bad: Python: ImportError Go: import cycle not allowed So why can’t JavaScript come out and say ⚠️ CircularDependencyError? It’s because JavaScript modules are designed to be loaded and executed on-the-fly. When your browser loads a web page and starts executing its first JavaScript file, it has no idea how many more files are still coming. Those files could still be sitting on servers on the other side of the world. This is a very different situation than a Go or Python program, where the import system can analyze the whole dependency tree before executing a single line of code. Stepping through a circular dependency in JavaScript The best way to explain the errors that JavaScript gives us is to step through a circular dependency scenario: Click to view a larger version of this image. Here’s what we see on each step: Step 1: On line 1 of index.js, execution pauses to download a.js so its value a can be imported. Step 2: Upon downloading a.js, execution continues in a.js but pauses on line 1 to download b.js, so its value b can be imported. Step 3: Upon downloading b.js, execution continues in b.js and finds an import on line 1 pointing back at a.js (a circular import). Step 4: a.js is already downloaded, but it has no exports defined because we haven’t executed anything past line 1 in a.js at this point. Thus, we cannot fulfill the import in b.js. Step 5: Execution continues in b.js with a remaining uninitialized. When a is called on line 3, the program errors with: ReferenceError: Cannot access 'a' before initialization. To summarize, the circular dependency results in code being executed with uninitialized values. This could result in various errors, like the ReferenceError above. Why do circular dependencies sometimes not cause errors? JavaScript’s imports are described as “Live Bindings.” This means that the imported value can start out uninitialized (due to circular dependencies) and become fully useable once the rest of code has been evaluated. In other words, some circular dependencies are error-free because they “work themselves out” before you call the affected code. I once worked in a code-base that was chock full of circular imports but none of them ever caused any issues. Why? It’s because all the code was defined in functions, none of which would be called until after everything was loaded. To demonstrate, we can update the last scenario to work in a similar way: Click to view a larger version of this image. Steps 1-4 are the same as above but things start to change at step 5: Step 5: a remains uninitialized but instead of being called directly, it is placed in a function definition (no error). Step 6: With b.js completed, execution in a.js continues down to line 3, which defines the export for a. From this point on, any code calling a will get an initialized value, as a result of the live bindings. Step 7: We call a() successfully, which in-turn calls b(). Ultimately, all the code gets called with no errors. To summarize, by the time we actually call that “uninitialized a”, the live bindings have updated its value and it’s no longer uninitialized. We’re safe because the value of a is only retrieved when the variable is actually used. Now, I wouldn’t recommend this as a way of fixing dependency issues. I’d feel better about removing the circular dependencies altogether. Still, I’ll bet there are plenty of production apps with circular dependencies that currently rely on this behavior. Preventing circular dependencies While JavaScript may not have built-in circular dependency checking, we still have options for preventing these issues. 3rd-party tools like madge and eslint-plugin-import can perform static analysis on your JavaScript codebase and detect circular dependencies before they become unwieldy. Some monorepo tools like NX and Rush have similar features built-in to their workflows. Of course, the best prevention is a well-organized codebase, with a clear hierarchy for shared code. What about Node / Bun / Webpack / etc? The examples I shared above are focused on the “ES modules in the browser” use-case, but JavaScript runs in a lot of different contexts. Server-side JavaScript doesn’t need to download its source over the network (making it more like Python) and bundling tools like Webpack can combine all the code into a single file. Are circular dependencies an issue in these scenarios? In short, yes. In my experiments, I was surprised to find that the error outcomes for browser, server, and bundler were basically the same. For example, with Webpack, the import statements were removed but the combined code still produced the same error: // b.js console.log('b.js:', a); // ReferenceError: Cannot access 'a' before initialization const b = 'B'; // a.js console.log('a.js:', b); const a = 'A'; I should also mention that while Node.js produced the same error when using the import syntax (ESM), it behaved differently when using the require syntax (CommonJS): $ node node-entry.cjs (node:13010) Warning: Accessing non-existent property 'Symbol(nodejs.util.inspect.custom)' of module exports inside circular dependency (Use `node --trace-warnings ...` to show where the warning was created) (node:13010) Warning: Accessing non-existent property 'constructor' of module exports inside circular dependency (node:13010) Warning: Accessing non-existent property 'Symbol(Symbol.toStringTag)' of module exports inside circular dependency It's nice that the warnings say "circular dependency" explicitly, when using CommonJS. This makes sense when you consider that CommonJS is an entirely different import system that doesn’t conform to the ECMAScript Modules spec. Comparing the two is comparing apples and oranges! Conclusion Circular dependencies can be confusing but it makes a lot more sense when you walk through the scenarios step by step. As always, nothing beats an experiment for getting a clear understanding of something like this. If you want a closer look at my test results, feel free to check out the repo.
“Civilization advances by extending the number of important operations which we can perform without thinking about them.” – Alfred North Whitehead Effective technology takes our most time-consuming tasks and moves them into the background so we can focus on other important things. We can see this happening on the civilization level with the infrastructure we’ve built to get inexpensive food, water, energy, and transportation. But it also works on the personal level. Whether it’s a high-quality dishwasher or well-designed personal-finance software, technology is our primary tool for saving time and redirecting our attention. But not all technology is successful at this. The power of technology is abstraction and a poor abstraction is worse than having no technology at all. Case in point: a smart light-bulb that never saves you enough time to recover the time you spent setting it up. Simon Sarris has a great post describing some of these issues: “Many modern devices (and apps) really excel at squishing tradeoffs into weird shapes. They are better thought of as little imps that sneak into homes and ask for more and more of your attention. They want to gently claw at your eyes and ears. They want to put notifications on your phone and remind you that you need to interact with them, or buy more of them, so that they might become even more convenient.” Simon Sarris, Careful Technology Much of our technology has small hidden costs. A new app on your phone, an audible hum in the background, a recommended cleaning every six months, recurring manual software updates, monthly emails in your inbox, increased risk of a breaker trip, reduced counter-top space, parts that need replaced, a small monthly fee, a new username and password, batteries to recharge, parts to recycle, etc, etc. Each cost seems small but with enough bad technology you face death by a thousand cuts. You lose time, peace, and other more difficult-to-quantify things, like “the cozier feeling of home.” We have to be discerning about the technologies we let into our lives. I’m tired of technology that trades one set of problems for another. I want that technology you don’t have to think about.
A few weeks ago, I was building a server-side API client. I had written the code and tested it in isolation. Everything looked good. Unfortunately, when I included it in the main service, I started seeing errors. I decided to try asking an AI tool for suggestions. I gave it the error message and a bunch of context. It gave me a solution with a detailed explanation. The errors went away. But the solution didn’t sit right with me. It was a bit complex, introducing more layers of code and various protections. The errors were gone, but I couldn’t clearly explain why it worked, and that was bothering me. While the code was being reviewed, I decided to take another look. I brought back the error and spent some time digging into the stack trace. That’s when I made the discovery: it was an environment issue. All I needed to do was set an environment variable and the issue would be fixed. The AI-provided code had been masking the real issue, quietly suppressing the error, and hiding the truth in its complexity. Now this is the part of the post where I’m supposed to criticize AI programming tools. I won’t be doing that. This isn’t an AI problem. I remember the first time I tried to fix a memory leak. Certain iPhone users would load the webpage, interact for a while, and then randomly the webpage would crash. We struggled to diagnose the issue (Safari’s devtools weren’t great at the time). We thought we fixed it several times but the issue kept coming back. Why? Because we didn’t understand the problem. We kept digging and eventually we found it: one of our dependencies was storing massive amounts of data on the window object and it wasn’t getting cleaned up. We added a cleanup step and the problem was gone for good. Since then, I’ve adopted a mantra: you can’t fix a problem you don’t understand. It doesn’t matter if the “fix” comes from AI, Stack Overflow, or trial-and-error. If I don’t understand the problem, I feel unsettled until I do.
Here are some things I made in 2024: Music Box Fun: Advanced Editing (a new major feature): Adds multiple-note selection for bulk operations on notes (like deletion, copy/paste, nudging and dragging) Also includes a “space editor” for arbitrarily adding/removing space anywhere in the song Includes other niceties like note highligting during playback and pitch highlight on hover Music Box Fun songs I made: Elliott’s Theme (Stardew Valley) in 15-note and 30-note versions Bluey Theme Song Jupiter Theme (The Planets) 11 new projects added to Let’s Get Creative, now available at a new domain name: https://letsgetcreative.today The Firefly Building in Minecraft (if you know, you know) 13 blog posts on bryanbraun.com, including a companion repo to my post on unusual Git IDs. I’m happy with this list. It was a year of many challenges. In February we got hit by a flooded basement and an emergency hospitalization which left my wife with limited mobility for a month. It was a difficult time and I’ll be forever grateful for the family and friends who helped us get through it. It was also a year of growth for my kids in particular, bringing many new parenting challenges. At the same time, I have so much to be grateful for. My wife and I hit our fifteenth anniversary and our relationship has grown stronger despite (or perhaps because of) the storms we’ve weathered. Every year brings new opportunities and challenges and it’s a privilege to have a committed partner that I can face them with. 🚵🏻♀️🚵🏻♂️ I wish you all the very best in 2025.
More in technology
My favorite memory of my M1 Pro MacBook Pro was the whole sensation of “holy crap, you never hear the fans in this thing”, which was very novel in 2021. Four years later, this MacBook Pro is still a delight. It’s the longest I’ve ever owned a laptop, and while I’d love to pick up the new M4 goodness, this dang thing still seems to just shrug at basically anything I throw at it. Video editing, code compiling, CAD models, the works. (My desire to update is helped though by the fact I got the 2TB SSD, 32GB RAM option, and upgrading to those on new MacBooks is still eye wateringly expensive.) But my MacBook is starting to show its age in one area: it’s not quiet anymore. If you’re doing anything too intensive like compiling code for awhile, or converting something in Handbrake, the age of the fans being quiet is long past. The fans are properly loud. (And despite having two cats, it’s not them! I clean out the fans pretty regularly.) Enter the thermal paste Everyone online seems to point toward one thing: the thermal paste on computers tends to dry up over the years. What the heck is thermal paste? Well, components on your computer that generate a lot of heat are normally made to touch something like a copper heatsink that is really good at pulling that heat away from it. The issue is, when you press these two metal surfaces against each other, even the best machining isn’t perfect and you there’s microscopic gaps between them meaning there’s just air at those parts, and air is a terrible conductor of heat. The solution is to put a little bit of thermal paste (basically a special grey toothpaste gunk that is really good at transferring heat) between them, and it fills in any of those microscopic gaps. The problem with this solution is after hundreds and hundreds of days of intense heat, the paste can dry up into something closer to almost a powder, and it’s not nearly as good at filling in those gaps. Replacement time The logic board! MacBook thermal paste isn’t anything crazy (for the most part, see below), custom PC builders use thermal paste all the time so incredibly performant options are available online. I grabbed a tube of Noctua NT-H2 for about $10 and set to taking apart my MacBook to swap out the aging thermal paste. And thankfully, iFixit has a tremendous, in depth guide on the disassembly required, so I got to it. Indeed, that grey thermal paste looked quite old, but also above and below it (on the RAM chips) I noticed something that didn’t quite seem like thermal paste, it was far more… grainy almost? Spottiness is due to half of it being on the heatsink It turns out, ending with my generation of MacBooks (lucky me!) Apple used a very special kind of thermal compound often called “Carbon Black”, which is basically designed to be able to bridge an even thicker gap than traditional thermal paste. I thought about replacing it, but it seems really hard to come across that special thermal compound (and do not do it with normal thermal paste) and my RAM temperatures always seemed fine (65°C is fine… right?) so I just made sure to not touch that. For the regular grey thermal paste, I used some cotton swabs and isopropyl alcohol to remove the dried up existing thermal paste, then painted on a bit of the new stuff. Disaster To get to the underside of the CPU, you basically need to disassemble the entire MacBook. It’s honestly not that hard, but iFixit warned that the fan cables (which also need to be unclipped) are incredibly delicate. And they’re not wrong, seriously they have the structural integrity of the half-ply toilet paper available at gas stations. So, wouldn’t you know it, I moved the left fan’s cable a bit too hard and it completely tore in half. Gah. I found a replacement fan online (yeah you can’t just buy the cable, need a whole new fan) and in the meantime I just kept an eye on my CPU thermals. As long as I wasn’t doing anything too intensive it honestly always stayed around 65° which was warm, but not terrifying (MacBook Airs completely lack a fan, after all). Take two A few days later, the fans arrived, and I basically had to redo the entire disassembly process to get to the fans. At least I was a lot faster this time. The fan was incredibly easy to swap out (hats off there, Apple!) and I screwed everything back together and began reconnecting all the little connectors. Until I saw it: the tiny (made of the same half ply material as the fan cable) Touch ID sensor cable was inexpicably torn in half, the top half just hanging out. I didn’t even half to touch this thing really, and I hadn’t even got to the stage of reconnecting it (I was about to!), it comes from underneath the logic board and I guess just the movement of sliding the logic board back in sheared it in half. me Bah. I looked up if I could just grab another replacement cable here, and sure enough you can… but the Touch ID chip is cryptographically paired to your MacBook so you’d have to take it into an Apple Store. Estimates seemed to be in the hundreds of dollars, so if anyone has any experience there let me know, but for now I’m just going to live happily without a Touch ID sensor… or the button because the button also does not work. RIP little buddy (And yeah I’m 99.9% sure I can’t solder this back together, there’s a bunch of tiny lanes that make up the cable that you would need experience with proper micro-soldering to do.) Honestly, the disassembly process for my MacBook was surprisingly friendly and not very difficult, I just really wish they beefed up some of the cables even slightly so they weren’t so delicate. The results I was going to cackle if I went through all that just to have identical temperatures as before, but I’m very happy to say they actually improved a fair bit. I ran a Cinebench test before disassembling the MacBook the very first time to establish a baseline: Max CPU temperature: 102°C Max fan speed: 6,300 RPM Cinbench score: 12,252 After the new thermal paste (and the left fan being new): Max CPU temperature: 96°C Max fan speed: 4,700 RPM Cinbench score: 12,316 Now just looking at those scores you might be like… so? But let me tell you, dropping 1,600 RPM on the fan is a noticeable change, it goes from “Oh my god this is annoyingly loud” to “Oh look the fans kicked in”, and despite slower fan speeds there was still a decent drop in CPU temperature! And a 0.5% higher Cinebench score! But where I also really notice it is in idling: just writing this blog post my CPU was right at 46°C the whole time, where previously my computer idled right aroud 60°C. The whole computer just feels a bit healthier. So… should you do it? Honestly, unless you’re very used to working on small, delicate electronics, probably not. But if you do have that experience and are very careful, or have a local repair shop that can do it for a reasonable fee (and your MacBook is a few years old so as to warrant it) it’s honestly a really nice tweak that I feel will hopefully at least get me to the M5 generation. I do miss Touch ID, though.
Meet the Creators of Choplifter, Wizardry, Castle Wolfenstein, Zaxxon, Canyon Climber, and the Arcade Machine
We’re excited to invite you to a brand-new workshop created in collaboration with Amazon Web Services (AWS). Whether you’re modernizing factory operations or tinkering with your first industrial project, this hands-on workshop is your gateway to building cloud-connected PLCs that ship data – fast. At Arduino, we believe in making advanced technology more accessible. That’s […] The post New AWS x Arduino Opta Workshop: Connect your PLC to the Cloud in just a few steps appeared first on Arduino Blog.
The term “mmWave” refers to radio waves with wavelengths on the millimeter scale. When it comes to wireless communications technology, like 5G, mmWave allows for very fast data transfer — though that comes at the expense of range. But mmWave technology also has some very useful sensing and scanning applications, which you may have experienced […] The post Concept Bytes’ coffee table tracks people and walks itself across a room when called appeared first on Arduino Blog.