Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
17
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...
3 weeks ago

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from Bryan Braun - Blog

Breaking down circular dependencies in JavaScript

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.

a month ago 21 votes
Technology you don’t have to think about

“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 month ago 21 votes
You can’t fix a problem you don’t understand

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.

2 months ago 34 votes
Made in 2024

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.

4 months ago 57 votes

More in technology

This plant always gets enough sunlight thanks to its robotic legs

Plants of all kinds are quite infamous for their inability to move, and this can be especially problematic for houseplants that rely on consistently sunny locations within a room in order to get enough light. Driven by wanting their plant to have the best possible growing conditions in their north-facing room, GitHub user MarinaXP has […] The post This plant always gets enough sunlight thanks to its robotic legs appeared first on Arduino Blog.

15 hours ago 1 votes
Vote for the May 2025 + Post Topic

Make your vote count.

22 hours ago 1 votes
Join Arduino at Automate 2025 to explore the future of automation

We’re heading to Automate 2025, the largest robotics and automation event in the Americas, happening May 12-15 at Huntington Place in Detroit – and we’re bringing a lineup of fresh innovations, live demos, and exciting new launches. You’ll find us in Booth #6632, right next to our partners at Weintek. This year is extra special […] The post Join Arduino at Automate 2025 to explore the future of automation appeared first on Arduino Blog.

18 hours ago 1 votes
Raycast does this

The gang gets to work defending their Mac login items. Who has the most minimal startup? Who's got the craziest apps? This episode has more new apps mentioned in any episode of Comfort Zone ever! Watch or listen now. Other Things Discussed Chris's hyper key video

19 hours ago 1 votes
Brightness and Contrast Adjustment of Tektronix TDS 500/600/700 Oscilloscopes

Introduction Finding the Display Tuning Potentiometers The Result Hardcopy Preview Mode Introduction Less than a week after finishing my TDS 684B analog memory blog post, a TDS 684C landed on my lab bench with a very dim CRT. If you follow the lives the 3-digit TDS oscilloscope series, you probably know that this is normally a bit of death sentence of the CRT: after years of use, the cathode ray loses its strength and there’s nothing you can do about it other than replace the CRT with an LCD screen. I was totally ready to go that route, and if I ever need to do it, here are 3 possible LCD upgrade options that I list for later reference: The most common one is to buy a $350 Newscope-T1 LCD display kit by SimmConn Labs. A cheaper hobbyist alternative is to hack something together with a VGA to LVDS interface board and some generic LCD panel, as described in this build report. He uses a VGA LCD Controller Board KYV-N2 V2 with a 7” A070SN02 LCD panel. As I write this, the cost is $75, but I assume this used to be a lot cheaper before tariffs were in place. If you really want to go hard-core, you could make your own interface board with an FPGA that snoops the RAMDAC digital signals and converts them to LVDS, just like the Newscope-T1. There is a whole thread about this the EEVblog forum. But this blog post is not about installing an LCD panel! Before going that route, you should try to increase the brightness of the CRT by turning a potentiometer on the display board. It sounds like an obvious thing to try, but didn’t a lot of reference to online. And in my case, it just worked. Finding the Display Tuning Potentiometers In the Display Assembly Adjustment section of chapter 5 of the TDS 500D, TDS 600C, TDS 700D and TDS 714L Service Manual, page 5-23, you’ll find the instructions on how to change rotation, brightness and contrast. It says to remove the cabinet and then turn some potentiometer, but I just couldn’t find them! They’re supposed to be next to the fan. Somewhere around there: Well, I couldn’t see any. It’s only the next day, when I was ready to take the whole thing apart that I noticed these dust covered holes: A few minutes and a vaccum cleaning operation later reveals 5 glorious potentiometers: From left to right: horizontal position rotation vertical position brightness contrast Rotate the last 2 at will and if you’re lucky, your dim CRT will look brand new again. It did for me! The Result The weird colors in the picture above is a photography artifact that’s caused by Tektronix NuColor display technology: it uses a monochrome CRT with an R/G/B shutter in front of it. You can read more about it in this Hackaday article. In real life, the image looks perfectly fine! Hardcopy Preview Mode If dialing up the brightness doesn’t work and you don’t want to spend money on an LCD upgrade, there is the option of switching the display to Hardcopy mode, like this: [Display] -> [Settings <Color>] -> [Palette] -> [Hardcopy preview] Instead of a black, you will now get a white background. It made the scope usable before I made the brightness adjustment.

23 hours ago 1 votes