This sentiment—that application programming interfaces are user interfaces, and that programmers are users—is why I’m spending so much effort improving the API of D3 4.0. I wrote about that in March: https://medium.com/@mbostock/what-makes-software-good-943557...
Please consider making it easier to debug as well. JavaScript has some excellent debuggers available to it-- D3 (at least the older version I used) seemed almost designed to defeat every useful feature of those debuggers.
It’s interesting—or frustrating?—that you should mention this, because D3 is explicitly designed with debugging (and debuggers, developer tools) in mind. I’d like to understand your frustrations better.
There are two main strategies that D3 uses to facilitate debugging.
First, whenever possible, D3’s operations apply immediately; for example, selection.attr immediately evaluates and sets the new attribute values of the selected elements. This minimizes the amount of internal control flow, making the debug stack smaller, and ensures that any user code (the function you pass to selection.attr that defines the new attribute values) is evaluated synchronously.
This immediate and synchronous evaluation of user code is in contrast to other systems and frameworks, including D3’s predecessor Protovis, that retain references to your code and evaluate them at arbitrary points in the future with deep internal control flow. I talk about this a bit in the D3 paper (and the essay I linked above): http://vis.stanford.edu/papers/d3 Even in the case of transitions, which are necessarily asynchronous, D3 evaluates the target values of the transition synchronously, and only constructs the interpolator asynchronously.
Second, and more obviously, D3 uses the DOM and web standards. It doesn’t introduce a novel graphical representation. This means you can use your browsers developer tools to inspect the result of your operations. Combined with the above, it means you can run commands to modify the DOM in the console, and then immediately inspect the result. D3’s standards-based approach has also enabled some interesting tools to be built, like the D3 deconstructor: https://ucbvislab.github.io/d3-deconstructor/
Without a more specific example to go on, it’s difficult to speculate. The only case where you’d get an error asynchronously should be in the case of transition.tween or an event listener—cases where the code is necessarily asynchronous—and not something like selection.attr or transition.attr, where the code can be evaluated synchronously.
It’s true that D3 uses closures and anonymous functions internally. But assuming you are using a debugger and the non-minified code, you can use that debugger to see exactly what the code is doing. To continue with the example of selection.attr, the implementation is here: