What Building an SDK and Reviewing an Open-Source Tool Taught Me About Developer Experience
By Kisho Jeyapragash
I approached the REST Countries SDK assignment expecting to spend most of my time on an API client. Instead, the setup path became the most useful part of the exercise. I created a generator scaffold, supplied a small OpenAPI definition and documented where generation stopped. Reviewing Aontu then gave me a way to examine the difference between a precise technical diagnostic and an explanation that helps a developer recover.
These were two technical assignments I completed in the context of Voxgig: an SDK-generation exercise and a critical review of an open-source tool. This article describes my own work and observations. It does not represent employment by Voxgig or an endorsement from the company. The repository evidence supports a generation attempt and review, not a finished, tested SDK package.
Start with a deliberately small API surface
I selected REST Countries and described two endpoints: GET /all and GET /name/{name}. The OpenAPI 3.0.3 file declares the v3.1 server, a required string path parameter for name lookup and array responses for both operations. Keeping the surface small made it possible to inspect the transition from an API definition into the generator model without first modeling every country field.
/name/{name}:
get:
operationId: getCountryByName
parameters:
- name: name
in: path
required: true
schema:
type: stringThe generated entity model maps the name operation to GET /name/{name}. In that model, the internal parameter is named id and linked to the original name parameter. That is a useful detail to inspect in generated output: an API parameter name and an SDK entity identifier may not be expressed in the same vocabulary. Before presenting a client interface to another developer, I would check that the documentation explains what the caller supplies.
Scaffolding is part of the product experience
My documented commands began with npm create @voxgig/sdkgen restcountries, followed by installing dependencies inside the generated .sdk directory and running generation. The first Windows run reported spawn npm ENOENT. npm was available in my environment, and a manual installation in the generated directory succeeded. That evidence narrows the observation: the failing operation was the generator spawning npm, not proof that npm was absent from the machine.
The scaffold contained project-level documents and generator internals, but the full target generation did not finish. I manually supplied the small OpenAPI definition after encountering an empty definition file. Parsing and model generation then completed, while a later test-model stage reported an unresolved include. These stages matter because calling the whole result either a successful SDK or a total failure would hide what actually happened.
A failed lookup is evidence, not a root cause
[aontu/multisource_not_found]: source not found: struct/test.aontuThe diagnostic identified the including file, highlighted the directive and listed attempted search paths. Those are useful observations. The difficulty was deciding the next recovery action: change the working directory, repair configuration, or return to an earlier setup stage? A message can accurately identify the failed lookup while still leaving the surrounding workflow hard to repair.
The later repository contains .sdk/test/struct/test.aontu. That does not establish that the file was present in the same state when the error occurred, and it does not prove a resolver defect. I also made manual model edits during troubleshooting, so later failures cannot all be treated as independent observations from an untouched scaffold. The review keeps those distinctions explicit rather than assigning every generator problem to Aontu.
What Aontu contributes to a model
Aontu combines data, constraints and defaults through unification. Compatible statements narrow the result; conflicting statements produce an error. This differs from ordinary configuration merging where a later value may simply replace an earlier value. My review focused on the practical value of keeping an allowed type and preferred default together, and the learning cost of interpreting that syntax.
port: *8080 | integer
port: 9090The example distinguishes a default from unrestricted replacement. The preferred value is 8080, but another integer is allowed; a fractional value conflicts with the constraint. Shared templates can similarly contribute rules to several named services. That avoids repeating the same requirement in each entry, while making it important for a reader to discover every statement that contributes to a resulting value.
Review the recovery path alongside the happy path
My recommendations were about making evidence actionable: a preflight check for the process invocation, a clear indication of incomplete scaffolding, and prominent context for include resolution. The review suggests showing the including file, working directory and configured search roots together. At the generator boundary, distinguishing an incomplete scaffold from an invalid model would help a newcomer decide whether editing the model is appropriate.
These are observations and recommendations about the workflow I examined, not claims about the current behavior of every version of the generator. The generated README describes client concepts such as direct and prepare, but those descriptions are not evidence that a usable target was produced in my attempt. I would verify an actual package, a runnable example and its failure cases before turning generated documentation into a completion claim.
Testing developer experience means testing recovery
The repository contains test-model inputs, but my report does not establish a passing SDK test suite. For a future SDK evaluation, I would check three different things: whether the project can be generated from a clean start, whether a simple example can call the selected endpoint, and whether a deliberate failure gives the caller a useful explanation. Each checks a different part of the onboarding path.
A useful issue report follows a similar structure. Record the command, environment, observed diagnostic, intermediate state and recovery attempts. Separate an observation from a proposed explanation. That gives a maintainer something they can investigate without first untangling which edits were made after the initial failure. It also makes critical feedback more constructive: the report explains where the developer lost the ability to choose a next step.
What I learned about developer relations
Developer experience is not only the convenience of a successful method call. It includes setup, generated vocabulary, documentation, errors and the ability to recover. My SDK attempt and Aontu review taught me to preserve the evidence behind a criticism and to make recommendations at the right boundary. A language diagnostic and a generator setup problem may be related, but they are not automatically the same defect.
That is the approach I want my technical writing to demonstrate: explain the mechanism, show the evidence and give another developer a useful way to reason about it. In this case, the most valuable output was not a claim of finished generation. It was a public account of the stages that worked, the step that stopped and the information that would make the next investigation more focused.