All work
GoAzureOpenTelemetryDistributed Systems·2024–Present

Infrastructure for the Energy Transition

Building systems that keep solar farms running when the weather doesn't cooperate.

CompanyNextpower LLC (formerly NEXTracker)RoleSoftware Engineer

Started as an intern building CI pipelines and hardening security. Stayed as a full-time engineer on the systems that keep utility-scale solar plants online: a failover controller that prevents outages during storms, an API optimization that cut response times by 65%, and the next generation of the on-site Datahub. Now building the AI layer on top: SAGE, an LLM support agent, and KnowledgeTree, a knowledge system that maps how the product's moving parts actually fit together.


What it is

Nextpower (formerly NEXTracker) makes the hardware and software that controls utility-scale solar farms: the systems that physically rotate panels to track the sun across thousands of acres. When those systems go down, energy output drops. When they go down during a storm, the damage can run into the millions.

I joined as an intern in May 2024. By February 2025 I was a full-time engineer. The work spans reliability engineering, API performance, and the kind of infrastructure decisions that matter more than they look.

Projects, and what they taught me

SAGEA support agent that does the first hour of triage before a human opens the ticket.System designLLM backend architectureAgent tool designTool use and retrievalGuardrails and observabilityGo

Support tickets arrive half described. Before an engineer can think about the real problem, they spend the first stretch of every ticket doing the same work: finding similar past issues, locating the relevant documentation, and reading the code to see what the system actually does. SAGE does that first pass.

It is a Go service built around a tool-use loop that I wrote myself. The model does not answer from memory. It calls narrow, read-only tools to pull similar tickets, documentation and code, then answers from what comes back. Owning the loop meant every call could be audited and budgeted. I also considered shipping it as an MCP tool server and decided against it: a server only exposes tools, and something still has to run the reasoning.

Two retrieval strategies live side by side. The curated KnowledgeTree wiki is small enough to hand the model directly. The codebase is not, so the model searches it instead. Choosing which problems get context stuffing and which get retrieval was one of the more useful decisions in the project.

SAGE can post a diagnosis, ask the reporter for what is missing, or open a draft pull request for a small, high-confidence fix. It can never merge. Every run has hard limits, and every action is logged. The lesson I keep coming back to: an LLM backend is mostly a systems problem. Retrieval quality, tool contracts, guardrails and observability decide whether it is useful. The model is one component, and usually not the one that breaks.

INTAKEREASONINGCONTROLOUTCOMESWHAT THE AGENT CAN READTicket intakeSweep + manual triggerQueueOne worker, dedupedAgent loopGo + Claude tool usehard caps: steps, tokens, timeDecision gateValidated in GoDiagnosisInternal noteNeeds infoAsks the reporterDraft PRNever mergedSimilar ticketsPast resolved issuesKnowledgeTreeCurated architecture wikiProduct codeBounded, read-only searchLogs + telemetryRead-only queriesAttachmentsFiles + screenshotsObservabilityEvery run and action is recorded

Agent loop

A tool-use loop around Claude. The model picks a tool, the loop runs it and feeds the result back, all within hard limits.

Design choiceI wrote the loop myself instead of using a prebuilt runner. I also weighed an MCP tool server and chose a self-contained agent: a server only exposes tools, and something still has to run the reasoning.

Hover or tap any block.

KnowledgeTreeA wiki of how the backend actually fits together, with a rule that every claim must be grounded.Context engineeringKnowledge graphsHallucination controlRetrieval designDocumentation as infrastructure

The codebase at Nextpower spans frontend, backend, and infrastructure that have been talking to each other for years without anyone writing down how. The result is institutional knowledge that lives in the heads of the people who have been here longest. When they are unavailable, the knowledge is unavailable.

KnowledgeTree is an attempt to fix that structurally. Built on Andrej Karpathy's LLM wiki idea with additions for our systems, it captures knowledge from code, lints it for accuracy, and syncs it when the code changes. The output is a wiki that describes the products end to end: how the pieces connect, not just what each one does.

The hard problem is not building it. The hard problem is keeping it useful. Context size: as the tree grows, so does the context passed to the model, until the signal drowns in things that are technically relevant and practically useless. The answer is hierarchy: store relationships at several levels of detail and load only the subgraph a question needs.

Staleness: code moves faster than documentation, so each node is tied to the git hash of the code it came from. When the source changes, the node is flagged. The wiki is either current or it tells you it is not. Confabulated relationships: given latitude, a model will invent plausible connections. The lint step requires every edge to be grounded in something observable, like an API call, a shared data model, or a message topic. If the grounding is not in the code, the edge does not exist.

Active–standby site controllersFailover for solar plants that lose their primary controller in a storm.Distributed systemsHigh availabilityState replicationSplit-brain handlingGo

The most consequential thing I have built here is an active–standby site controller system in Go. Solar plants can lose their primary controller during storms. The failover needs to be seamless: no manual intervention, no data loss, no gap in control.

Getting that right meant thinking carefully about state replication, split-brain scenarios, and what "seamless" actually means in a system where the hardware does not stop moving.

Working on physical infrastructure sharpens your thinking about reliability in a way that pure software problems do not. The failures here have real-world consequences measured in energy and dollars. You cannot roll back a storm.

API performanceCutting average response time from 1100ms to 385ms by finding where the time actually went.ObservabilityOpenTelemetryPerformance profilingAzure MonitorRoot cause analysis

I optimized API performance using OpenTelemetry and Azure Monitor Application Insights, reducing average response time from 1100ms to 385ms, a 65% improvement.

The gains came from understanding where the latency was hiding, not from blanket caching. Performance work is mostly detective work. The improvement was not clever code. It came from finding the three places where we were doing work twice and stopping.

Hardware reuse across sitesMoving a Datahub from one site to another without the manual decommissioning.Product thinkingWorkflow designBackend featuresOperational cost

Solar operators move equipment between sites. Previously that meant manual decommissioning workflows.

I built a feature that lets users disconnect a Datahub from one site and reconnect it to another, saving thousands of dollars per reuse and a meaningful amount of operational friction. It was a reminder that some of the most valuable engineering is removing a procedure people had accepted as the cost of doing business.

Gen3 DatahubThe next generation of the on-site hardware that every plant depends on.Embedded LinuxSystem images and firmwareSecure by default designFleet managementHardware and software boundaries

The Datahub is the device at a solar site that collects data and talks to the cloud. When it misbehaves, nobody is standing next to it. It is in a field, possibly a long drive away, and the plant keeps running whether or not it is healthy.

I am working on its third generation, from the system image and firmware up through how the device is configured and managed from the cloud. A change here ships to thousands of machines you cannot easily touch, so the design bar is different from a web service. Defaults have to be safe, upgrades have to be recoverable, and nothing can depend on someone being on site.

What this taught me is how much of reliability is decided before the first line of application code runs. The image, the boot path, and the management model set the ceiling for everything built on top of them.

Datahub migrationMoving a live site onto new hardware without losing its history.Data migrationDistributed stateIdempotent toolingOperator UXBackward compatibility

When a Datahub is replaced, the site's data has to follow it. That data does not live in one place. It spans a relational database, a time series store, and a cache, and each one has different rules about what consistent means.

The migration tooling makes the move repeatable instead of a careful manual job. It handles moves between hardware generations, runs as a detached job so a dropped connection does not end it, and gives operators an interface so the procedure is the same every time, whoever is running it.

The principle I designed around: a migration you can safely run twice is worth more than one that is fast. Partial failure is the normal case, not the exception, so every step has to be resumable and every result checkable.

Read-only Linux access for customersLet customers see inside the box without letting them change it.Least privilegeAccess control designLinux systemsAPI design in GoThreat modeling

Some customers need visibility into the system running their Datahub. They do not need, and should not get, the ability to change it. That sounds like a checkbox. It is not.

Read-only is a property you have to prove, not a name you give an account. I am building a restricted access path for newer Datahubs, along with the provisioning and management pieces that make it safe to hand out and safe to revoke. The work sits across the operating system, backend services in Go, and the cloud and UI that control it.

It sharpened how I think about least privilege: start from what the user needs to see, then work backwards to everything that would let them do more, and close each of those on purpose.

What I built: internship

As an intern, I built a CI pipeline using Azure DevOps that automated builds across multiple projects, cutting developer workload by 10 hours a week. I also led static and composition analysis across our dependencies, reducing high and medium security vulnerabilities by 92%. The interesting part wasn't the tooling. It was understanding why those vulnerabilities existed and designing checks that would catch them structurally, not just reactively.