DEV Community

Geek Consulting
Geek Consulting

Posted on Originally published at geekconsulting.au on

I’m a Salesforce Architect, Not a Rust Developer. I Just Shipped Four PRs to a Rust Project Anyway

I’ve spent my career in the Salesforce ecosystem: data models, integration patterns, org governance, the sharp edges of the platform’s APIs. What I have not spent my career doing is writing Rust, or TypeScript build tooling, or AES-GCM credential encryption.

And yet, over five days in July, I contributed a complete Salesforce sink connector to Duckle — an open-source, local-first ETL studio written in Rust on top of DuckDB, with a TypeScript/React frontend. Four pull requests merged, one genuine engine bug discovered and filed along the way.

I didn’t write the code. Claude (Anthropic’s coding agent, running in Claude Code) wrote the code. My job was everything around the code — and it turns out that’s where most of the leverage is.

The setup

Duckle is exactly the kind of tool that appeals to a data architect: visual pipelines, native DuckDB speed, no cloud dependency, no lock-in. It had 360+ components. It did not have a Salesforce connector worth the name — and Salesforce is where an enormous amount of enterprise data lives.

I knew precisely what a good Salesforce sink needed to do, because I’ve lived with the alternatives (Data Loader, MuleSoft, half a dozen middleware products) for years:

  • Insert, update, upsert, delete via the sObject Collections REST API, with upsert keyed on an external ID field — the pattern every real migration uses
  • OAuth 2.0 Client Credentials flow, not username/password or copy-pasted session tokens
  • Per-record error handling — Salesforce fails records individually, and a tool that fails the whole batch on one DUPLICATE_VALUE error is useless
  • Data Loader-style success/error CSV files, because that’s the artifact every Salesforce data person expects at the end of a load

What I didn’t know was how Duckle’s Rust plan/execution engine worked, how its frontend palette and field-manifest system generated UI forms, or how its desktop app encrypted saved credentials. That’s a real stack: a RuntimeSpec enum dispatched through a DuckDB-backed engine, a code-generated component catalog, a Tauri-style desktop shell.

The bet was that the division of labour could be clean: I supply the what and the why, the agent supplies the how, and I verify the result against a real Salesforce org.

What actually shipped

PR #165 — the Tier 1 sink (957 lines across 12 files, merged the next morning). A new snk.salesforce component: engine spec, REST envelope building, per-record result parsing, three mock-server integration tests, frontend palette entry and configuration form, and documentation. My input was a spec: which API, which operations, how batching should chunk (200 records per sObject Collections call), what an upsert on External_ID__c must look like on the wire, and what error text a Salesforce admin would actually understand.

The design conversation that mattered more than code. After the sink merged, I opened a discussion about authentication (#166): the sink shouldn’t need a hand-minted bearer token; it should mint its own via Client Credentials at run time. I asked the maintainer two design questions — what the config payload should look like, and where in the architecture token resolution should happen (in the engine, or in the host process that launches runs). He answered both, then implemented that first stage himself. That’s open source working as intended: the contribution was the design pressure and the domain requirements, and the maintainer built it his way, faster than a PR round-trip would have.

PR #171 — saved encrypted connections (1,434 additions across 39 files, merged within two hours). This was the deep one: a new shared Rust crate (duckle-secrets) extracted from the desktop app’s credential store, AES-GCM encryption for client secrets, run-time resolution of connection references across four different host binaries (desktop, scheduler, runner, headless server), and a frontend connection picker that only ever passes a reference — so a pipeline file never contains a secret. I could not have written this. But I could specify the security property that mattered: credentials must never land in a pipeline JSON file that someone commits to git, because I have watched that exact incident happen in real Salesforce projects.

PR #176 — visibleWhen conditional form fields (merged). A pure frontend feature with no Salesforce code in it at all — configuration fields that show or hide based on other fields’ values. It exists because the Salesforce connector needed it: once you support both bearer-token and Client Credentials auth, showing both sets of fields at once is confusing and wrong. The Salesforce use case justified a general capability the whole component catalog can now use. Contributions compound like that.

Issue #170 — a real bug, found by testing like an architect. While building live test pipelines, an edge case I always test — what happens when the source query returns zero records? — broke Duckle’s REST source entirely: it materialized a single raw json column and every downstream SQL step failed with a binder error. Pre-existing bug, nothing to do with our code. I filed it with a reproduction; the maintainer fixed it within two days. Knowing which edge cases matter is domain knowledge too.

PR #181 — resultsPath result files (615 lines, 8 unit tests + 3 integration tests, merged in under two hours). Data Loader-style stamped success/error CSVs — Account_upsert_20260715T031500Z_success.csv — written on every exit path, accumulating across runs rather than overwriting. The accumulate-don’t-overwrite decision was mine, made while testing: a scheduled pipeline that silently overwrites last night’s error file is a tool that loses audit history.

Alongside the PRs, we built a 17-scenario live test suite that runs the full operation-by-auth-mode matrix against a real Salesforce org — with a credential-free design (${ENV:…} placeholders) so the suite itself can live in the repo without a secret in sight.

How the collaboration actually worked

The honest version, not the demo-reel version:

I was the product manager, architect, and QA. The agent was the engineering team. Every session started with me describing behaviour in Salesforce terms: “upsert must use the external ID in the URL path, not the body”, “a 200 response can still contain per-record failures”, “field truncation errors need to name the field”. Claude translated that into idiomatic Rust and TypeScript that matched the existing codebase’s conventions — which it read and learned first.

Testing was my half of the loop, and it was not optional. Every feature got exercised three ways: the agent’s own unit and mock-server tests, the live suite against my test org, and me personally clicking through the desktop app — configuring a connection, running a load, forcing real DUPLICATE_VALUE errors, checking the error CSV said something useful. The agent is good; it is not a substitute for a domain expert watching the actual product do the actual thing. Several of my best inputs (the accumulate-vs-overwrite call, the zero-records bug) came from testing, not from specifying.

Small PRs, design questions first, maintainer’s conventions always. We asked before building, kept each PR to one concern, matched the repo’s formatting rather than running our own formatter over it (a repo-wide cargo fmt would have touched 11,000 unrelated lines — the agent flagged this before I would have known to care), and flagged known trade-offs in the PR descriptions rather than hoping reviewers wouldn’t notice. All four PRs merged in under 24 hours each — the last in 95 minutes. Maintainers respond to contributors who respect their time; that’s true whether the code came from a human or an agent.

The friction points were real but manageable. The agent occasionally needed steering back to the maintainer’s stated architecture. The Windows/Rust toolchain setup (MSVC, vendored DuckDB CLI, PATH quirks) consumed a real afternoon. And AI attribution in open source is still an unsettled question — norms vary project to project, and it’s worth having that conversation with a maintainer early rather than assuming.

What this means, maybe

The conventional wisdom is that open source has a contribution funnel problem: the people with the deepest domain knowledge about what a tool should do are rarely the people fluent in the tool’s implementation language. A Salesforce architect knows exactly what a Salesforce connector needs; a Rust systems programmer knows exactly how to build it; those are almost never the same person, and historically the connector didn’t get built.

That wall just got a lot shorter. Not gone — I still needed to understand APIs deeply, make architectural judgment calls, test rigorously, and communicate with a maintainer like a professional. The five days were full days. But the specific skill of writing Rust stopped being the gate.

If you’re a domain expert who’s been watching an open-source project miss the feature you need: the excuse inventory is shrinking. Pick the project. Write the spec you already have in your head. Direct the agent. Test like your production org depends on it — because eventually, someone’s will.

The work described: Duckle PRs #165, #171, #176, #181, and issue #170. Built with Claude Code. Tested against a real Salesforce org with an External Client App on the Client Credentials flow.

Top comments (0)