<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Faishal Wahiduddin: Workflows and Infra]]></title><description><![CDATA[Faishal Wahiduddin: Workflows and Infra]]></description><link>https://faishalwahiduddin.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 23 Oct 2026 01:27:38 GMT</lastBuildDate><atom:link href="https://faishalwahiduddin.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Best Interface Was One I Didn't Build: WhatsApp on a Construction Site]]></title><description><![CDATA[The first version of any B2B platform has a login page. Then a dashboard. Then, if the product touches anyone outside the office, a long quiet argument…]]></description><link>https://faishalwahiduddin.hashnode.dev/the-best-interface-was-one-i-didn-t-build-whatsapp-on-a-construction-site</link><guid isPermaLink="true">https://faishalwahiduddin.hashnode.dev/the-best-interface-was-one-i-didn-t-build-whatsapp-on-a-construction-site</guid><category><![CDATA[Product Management]]></category><category><![CDATA[whatsapp]]></category><category><![CDATA[n8n]]></category><category><![CDATA[automation]]></category><dc:creator><![CDATA[Faishal Wahiduddin]]></dc:creator><pubDate>Sun, 18 Oct 2026 13:23:43 GMT</pubDate><content:encoded><![CDATA[<p>The first version of any B2B platform has a login page. Then a dashboard. Then, if the product touches anyone outside the office, a long quiet argument about why nobody is using it.</p>
<p>AutoKon verifies construction progress with photo evidence and uses that evidence to gate contractor disbursements across thousands of housing units. The evidence has to come from the site, from people standing in the mud with a phone, often on a patchy connection, often not the same people from one week to the next.</p>
<p>I did not build them a portal. They submit over WhatsApp.</p>
<h2>Count the real cost of an account</h2>
<p>A portal looks free when you are the one building it. The cost lands somewhere else:</p>
<ul>
<li><p>Someone has to create an account for every field worker, and deactivate it when they rotate off.</p>
</li>
<li><p>Someone has to train them, in person, on a screen they will use a handful of times.</p>
</li>
<li><p>The app has to go on a phone that may already be near full.</p>
</li>
<li><p>Each of those steps is a place where the evidence does not get submitted, which means a disbursement does not get processed, which means somebody's payment is late.</p>
</li>
</ul>
<p>None of that appears in a sprint estimate. It appears in adoption.</p>
<p>Meanwhile every one of those workers already had WhatsApp open, already knew how to send a photo, and had used it that morning. The interface was installed and understood. The only question was whether I was willing to meet it.</p>
<h2>What the architecture actually looks like</h2>
<p>Field submissions come in through a WhatsApp-first flow built on n8n and Botpress. The conversation collects what the record needs (which unit, which stage, the photo) and hands a structured submission to the platform. From there the evidence enters the vision pipeline, gets graded by the evaluation stage, and lands in a five-role approval workflow.</p>
<p>Approvers work through guided chat too. Someone with four items to approve does not open a dashboard, find the queue and filter it. The items come to them in order, photo attached, decision reduced to a reply.</p>
<p>The dashboards still exist: S-curve and earned-value views for the people who plan and audit. Planning and auditing happen at a desk with a large screen. Capturing evidence and approving it do not. Two audiences, two surfaces, and only one of them had to be built from scratch.</p>
<h2>Conversational does not mean unstructured</h2>
<p>The failure mode of chat interfaces is that they accept anything and produce garbage, and a financial verification pipeline cannot afford that.</p>
<p>So the flow is guided rather than open. It asks one thing at a time. It rejects a submission with no unit reference instead of guessing. It never takes a free-text status in place of a photo. What reaches the platform is as structured as anything a form would have produced. The structure just lives in the conversation design instead of in a set of input fields.</p>
<p>Choosing WhatsApp did not remove the work of designing a data-entry flow. It moved that work, and made it harder, because you cannot lean on the layout to explain the model.</p>
<h2>The leverage</h2>
<p>I built and ran AutoKon's platform solo, which made the channel decision unusually clear: every surface I built was a surface I would maintain, support and onboard people onto, by myself, indefinitely.</p>
<p>Field workers across three timezones now submit evidence without a single account being provisioned, and the system that gates real money stays fed.</p>
<h2>Before you build another screen</h2>
<p>Ask who is on the other side and where they are standing, because a muddy site at 7am is not a desk. Add up what an account really costs across provisioning, training, installation and rotation, and work out who absorbs that cost. Check whether the task is recurring and complex or occasional and simple, since dashboards earn their keep on the first and conversations win the second. And if the structure is not going into a form, be able to say where it is going instead.</p>
<p>The most useful interface decision I made that year was noticing that the right one was already on everybody's phone.</p>
]]></content:encoded></item><item><title><![CDATA[Twelve Hours Apart: Leading DoCheck's Engineering Team From the Other Side of the Planet]]></title><description><![CDATA[From 2021 to 2023 I was CTO of DoCheck, a productivity SaaS with a 20+ engineering team in Indonesia. I was in Orlando, finishing an M.S. in Computer…]]></description><link>https://faishalwahiduddin.hashnode.dev/twelve-hours-apart-leading-docheck-s-engineering-team-from-the-other-side-of-the-planet</link><guid isPermaLink="true">https://faishalwahiduddin.hashnode.dev/twelve-hours-apart-leading-docheck-s-engineering-team-from-the-other-side-of-the-planet</guid><category><![CDATA[leadership]]></category><category><![CDATA[remote]]></category><category><![CDATA[engineering-management]]></category><category><![CDATA[async]]></category><dc:creator><![CDATA[Faishal Wahiduddin]]></dc:creator><pubDate>Sun, 18 Oct 2026 13:22:44 GMT</pubDate><content:encoded><![CDATA[<p>From 2021 to 2023 I was CTO of DoCheck, a productivity SaaS with a 20+ engineering team in Indonesia. I was in Orlando, finishing an M.S. in Computer Science at the University of Central Florida on a Fulbright scholarship.</p>
<p>Indonesia runs roughly twelve hours ahead of Florida. My team's afternoon was my middle of the night; their standup was my bedtime. No scheduling trick makes that go away. With a twelve-hour offset the only overlap you get is the overlap you deliberately buy, and you pay for it with somebody's evening.</p>
<p>Velocity went up 30% over that period. Not because of the distance, but because the distance made it impossible to keep getting away with several things.</p>
<h2>The time zone audits your process</h2>
<p>A co-located team runs on ambient information. Someone overhears a decision. Someone taps a shoulder. A ticket says "fix the thing we discussed" and everyone knows which thing.</p>
<p>Move the lead twelve hours away and each of those shortcuts becomes a twenty-four-hour round trip. A ticket that needs one clarifying question does not lose ten minutes. It loses a day.</p>
<p>That is a harsh audit, and a useful one. Everything vague in our process now produced a delay we could measure, so we fixed those things in self-defense:</p>
<ul>
<li><p>Decisions written down with the reasoning attached, not just the outcome, because the person reading could not ask me why.</p>
</li>
<li><p>Tickets that could be started without me. If an engineer had to wait for an answer before beginning, the ticket was not ready.</p>
</li>
<li><p>Agreed definitions of done, so "finished" did not need adjudicating across a day boundary.</p>
</li>
</ul>
<p>None of that is novel. What was new was having no way to avoid it.</p>
<h2>Spend the overlap on what only humans can do</h2>
<p>We had a narrow band of shared waking hours, and the instinct is to fill it with status. Status is the worst possible use of overlap. It is exactly the information that survives being written down.</p>
<p>So status moved to writing, and live time went to the things that genuinely degrade in text: disagreements about architecture, someone stuck and not saying so, career conversations, feedback whose tone lands wrong in a message. I rebuilt our agile rituals around that split. Rituals that existed to broadcast information became documents. Rituals that existed to build shared judgment kept their synchronous slot.</p>
<h2>Automation is a leadership tool</h2>
<p>When you cannot be present, every manual step between commit and production becomes a step waiting on someone who might be asleep.</p>
<p>That is why we invested in CI/CD upgrades. The goal was not developer comfort. It was removing the human bottlenecks that a twelve-hour offset turns into day-long stalls. A pipeline that checks quality on its own does not need me awake.</p>
<p>The same logic drove the infrastructure work. We migrated key workloads to AWS serverless with disciplined resource governance and cut infrastructure costs by 20%. Some of that was straightforward FinOps. Some of it was serverless removing a class of operational babysitting that someone would otherwise have done at an inconvenient hour.</p>
<h2>What the team gained that I did not plan for</h2>
<p>Autonomy. When the lead is unreachable twelve hours a day, decisions get made without the lead. That is uncomfortable exactly once: the first time someone makes a call you would have made differently and you have to choose between reversing it and backing it.</p>
<p>Backing it, then fixing the guidance that made the call ambiguous, is how a team stops needing you for that class of decision. Distance forced that on me faster than proximity ever would have. By the end the team made most calls without me and escalated a much smaller, much better-chosen set.</p>
<h2>What I kept</h2>
<p>I have led co-located teams since. These stayed.</p>
<ol>
<li><p>Write the decision and the reasoning. The reasoning is what lets someone extend the decision to a case you did not anticipate.</p>
</li>
<li><p>Never spend synchronous time on what a document does better.</p>
</li>
<li><p>Treat every manual gate as a future stall and automate accordingly.</p>
</li>
<li><p>A ticket nobody can start without asking you is not ready.</p>
</li>
<li><p>Back the judgment calls your team makes in your absence, then fix the ambiguity that made them hard.</p>
</li>
</ol>
<p>The gap did not make us faster. It removed the option of postponing the changes that did.</p>
]]></content:encoded></item><item><title><![CDATA[Before AI Moves Money: The Eval Gate I Built for AutoKon]]></title><description><![CDATA[Most AI features fail quietly. A summary is a little off, a label is wrong, a suggestion gets ignored. Nobody files a ticket and the product keeps working.]]></description><link>https://faishalwahiduddin.hashnode.dev/before-ai-moves-money-the-eval-gate-i-built-for-autokon</link><guid isPermaLink="true">https://faishalwahiduddin.hashnode.dev/before-ai-moves-money-the-eval-gate-i-built-for-autokon</guid><category><![CDATA[llm]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[fintech]]></category><dc:creator><![CDATA[Faishal Wahiduddin]]></dc:creator><pubDate>Sun, 18 Oct 2026 13:21:14 GMT</pubDate><content:encoded><![CDATA[<p>Most AI features fail quietly. A summary is a little off, a label is wrong, a suggestion gets ignored. Nobody files a ticket and the product keeps working.</p>
<p>AutoKon is not that kind of product. It is a financial verification pipeline for Indonesian property developers: a field worker photographs a housing unit, a vision-language model reads the photo, and that reading is part of what releases a contractor's payment. A wrong description does not degrade the experience. It moves money to the wrong place.</p>
<p>That one property changed how I built everything else.</p>
<h2>The pipeline does not end at the model</h2>
<p>The obvious architecture is photo in, VLM out, decision made. Claude Opus Vision is very good at describing a construction site. It will tell you the walls are rendered, the roof trusses are up, the floor screed is done. It will occasionally tell you all of that, with the same confidence, about a photo showing something else. Rarely, but often enough to matter.</p>
<p>So every AI-generated description passes through an LLM-as-judge evaluation stage before it goes anywhere near a disbursement decision. A second model grades the first one against the image and the claim, and the item proceeds only if it clears the bar.</p>
<p>The judge is not a quality dashboard bolted on after launch. It sits in the payment path. If the eval stage is down, disbursements wait. That is the right failure mode for this domain, and settling it early is what made the rest of the design tractable.</p>
<h2>What the judge is actually measuring</h2>
<p>Most eval work I see optimizes for whether the answer is good. For a payment gate that is the wrong question.</p>
<p>The question is whether the description is grounded: whether every claim in it is supported by what is in the photograph. A description can be well written, plausible and useful, and still assert a completed stage that is nowhere in the frame. That is the failure that must never reach the disbursement engine.</p>
<p>So the judge gets a narrow prompt. Is each claim here supported by this image? It is never asked whether the description reads well. Ungrounded claims fail the item even when the writing is excellent, and most of the value sits in that narrowing. A broad quality rubric produces a judge that agrees with the generator far too often, because both models share the same optimistic priors.</p>
<h2>The number that makes a judge worth having</h2>
<p>An automated judge is only worth having if it agrees with a human reviewer, and you have to be able to say by how much.</p>
<p>The pipeline targets Cohen's κ ≥ 0.80 against human labels, which is substantial agreement on any reading of the scale. That is an operating threshold, not a figure for a slide. Below it the judge is a source of false confidence rather than a control, and the honest response is to widen human review until the rubric or the labels improve.</p>
<p>Three things I learned getting there. Human labeling time is scarce, so spend it where the generator and the judge disagree rather than on the cases that were never in doubt. Ambiguous rubrics show up as low κ well before they show up as bad outcomes; when agreement dropped, the cause was almost always a rubric that let two reasonable reviewers score the same photo differently, not a model that got worse. And the input distribution keeps moving. New site types, new phone cameras and the rainy season all change what the camera sees, so an eval suite frozen at launch decays.</p>
<h2>Where the humans sit</h2>
<p>The eval gate does not replace approval. AutoKon runs a five-role photo-evidence approval workflow, and field submissions arrive over WhatsApp through an n8n and Botpress flow rather than through a portal that every field worker would need an account for and training on.</p>
<p>The AI approves nothing. It clears the obviously complete, obviously grounded evidence out of a reviewer's queue and flags what needs a person. Reviewers get the ambiguous cases with their context attached, which is where human judgment earns its cost. That is what let one engineer run a platform across thousands of housing units and three timezones without the review burden growing in step with the number of units.</p>
<h2>What transfers</h2>
<p>Not every product moves money, but the questions carry. What is the worst thing a confident wrong answer can do here, and does your design start from that or from what the model can do? Is your metric measuring correctness against the source, or plausibility? Can you say what your agreement with a human actually is? Are the people still in the loop getting the hard cases or the easy ones?</p>
<p>The model was never the hard part of this build. The hard part was the structure around it, and being willing to let that structure stop the happy path when it has to.</p>
]]></content:encoded></item><item><title><![CDATA[Keeping n8n Workflows Sane Across Self-Hosted Stacks and n8n Cloud]]></title><description><![CDATA[n8n makes it easy to build a workflow in the browser and switch it on. That is great on day one. By the time you have several instances, a few dozen…]]></description><link>https://faishalwahiduddin.hashnode.dev/keeping-n8n-workflows-sane-across-self-hosted-stacks-and-n8n-cloud</link><guid isPermaLink="true">https://faishalwahiduddin.hashnode.dev/keeping-n8n-workflows-sane-across-self-hosted-stacks-and-n8n-cloud</guid><dc:creator><![CDATA[Faishal Wahiduddin]]></dc:creator><pubDate>Thu, 15 Oct 2026 13:06:25 GMT</pubDate><content:encoded><![CDATA[<p>n8n makes it easy to build a workflow in the browser and switch it on. That is great on day one. By the time you have several instances, a few dozen workflows, and at least one of them touching money, editing live in the UI becomes the riskiest habit in your stack.</p>
<p>At AutoKon, where I was CTO and the only engineer, I ran self-hosted n8n as two separate stacks, one for the app and one for ops, on a dedicated VPS with Docker and Caddy. Those ran alongside n8n Cloud with staging and production instances. Workflow JSON lived in a Git repo, and changes moved from staging to production through a promotion step, with extra care around the Opname and BASTP approval chain that sat in front of contractor payments.</p>
<p>This post covers the principles behind that setup and the general method I would use to rebuild it. I am describing the approach, not publishing the exact repo layout or scripts, and the commands below come from the n8n CLI documentation. I have not run them against a live instance while writing this, so check them against your n8n version.</p>
<h2>Why split app and ops</h2>
<p>The app stack runs workflows the product depends on: webhooks from users, data intake, anything a customer would notice if it stopped. The ops stack runs workflows that look after the system: health checks, alerts, housekeeping, reports for yourself.</p>
<p>Keeping them apart buys three things:</p>
<ul>
<li><p>Blast radius. A runaway ops job cannot starve product webhooks of workers or memory.</p>
</li>
<li><p>Different change rhythms. Ops workflows change often and casually. App workflows should change slowly and with review.</p>
</li>
<li><p>One can watch the other. If the app stack goes down, the ops stack is still alive to tell you.</p>
</li>
</ul>
<p>Running n8n Cloud next to self-hosted adds a separate failure domain and a managed staging and production pair. The cost is that you now have more than one place where a workflow might live, which is exactly why Git has to be the source of truth.</p>
<h2>Rule 1: Git is the source of truth</h2>
<p>Every workflow should exist as JSON in a repository, one file per workflow. The editor is where you draft. The repo is what you trust.</p>
<p>On a self-hosted instance, the n8n CLI can export everything as separate files:</p>
<pre><code class="language-bash">n8n export:workflow --backup --output=workflows/
</code></pre>
<p><code>--backup</code> is shorthand for exporting all workflows as separate, pretty-printed files. Importing a directory back works the same way:</p>
<pre><code class="language-bash">n8n import:workflow --separate --input=workflows/
</code></pre>
<p>n8n Cloud does not give you a shell, so for Cloud instances you would pull and push workflow JSON through n8n's public REST API instead, using an API key generated in that instance's settings.</p>
<h2>Rule 2: Normalize JSON before committing</h2>
<p>Raw exports are noisy. Timestamps, version IDs and pinned test data change on every save, so diffs fill up with lines that mean nothing. A small <code>jq</code> pass keeps diffs about logic:</p>
<pre><code class="language-bash">for f in workflows/*.json; do
  jq -S 'del(.updatedAt, .createdAt, .versionId, .pinData)' "$f" &gt; "$f.tmp" &amp;&amp; mv "$f.tmp" "$f"
done
</code></pre>
<p><code>-S</code> sorts keys so the same workflow always serializes the same way. Dropping <code>pinData</code> matters for more than noise: pinned data is sample input you used while building, and it can contain real payloads you do not want in Git.</p>
<p>Check which fields your n8n version emits before copying this list. The point is to strip anything that changes without the workflow's behavior changing.</p>
<h2>Rule 3: Keep secrets and environment values out of the workflow</h2>
<p>Exported workflows reference credentials by ID and name. The secret values are stored separately in the instance and are not part of the workflow export, which is what you want. Never commit a decrypted credentials export.</p>
<p>The catch is that credential IDs differ between instances. A workflow exported from staging can point at a credential ID that does not exist in production. Two habits help:</p>
<ul>
<li><p>Give credentials the same name in every environment, so mapping them during promotion is mechanical.</p>
</li>
<li><p>Keep environment-specific values, such as base URLs, webhook targets and spreadsheet IDs, in one clearly marked place per environment instead of scattered across node parameters. Promotion should not require hand-editing nodes.</p>
</li>
</ul>
<h2>Rule 4: Promote, never edit production</h2>
<p>The promotion path I recommend:</p>
<ol>
<li><p>Build or change the workflow on staging.</p>
</li>
<li><p>Export, normalize, and open a pull request. Read the diff like code.</p>
</li>
<li><p>Test on staging with realistic inputs. For webhook workflows, keep a small set of fixture payloads in the repo and replay them.</p>
</li>
<li><p>Merge, then import the same JSON into production and remap credentials.</p>
</li>
<li><p>Activate in production as a separate, deliberate step.</p>
</li>
</ol>
<p>For workflows that sit inside a financial approval chain, add guardrails on top: a second look at the diff even when you are a team of one (sleeping on it counts), a staging run that walks every approval branch, and a rule that production is never edited in the UI. If someone does hot-fix production, export it straight back into the repo the same day so Git stays honest.</p>
<h2>Rule 5: Watch it like any other service</h2>
<p>n8n can expose a Prometheus metrics endpoint when metrics are enabled in its configuration. At AutoKon, Prometheus and Grafana ran over WireGuard, so the dashboards and scrape traffic stayed on a private network. Whatever you use, track at least:</p>
<ul>
<li><p>failed executions per workflow</p>
</li>
<li><p>queue depth or execution backlog</p>
</li>
<li><p>container restarts and memory</p>
</li>
</ul>
<p>Then let the ops stack alert you about the app stack, and something outside both stacks alert you if the ops stack itself goes quiet.</p>
<h2>Pitfalls I would plan for</h2>
<ul>
<li><p>ID collisions on import. Importing a workflow whose ID already exists can replace the existing one. That is what you want for promotion and a nasty surprise anywhere else, so confirm the behavior on your version first.</p>
</li>
<li><p>Webhook URLs change per instance. Anything external calling a staging webhook has to be repointed for production.</p>
</li>
<li><p>Version drift between self-hosted and Cloud. Cloud updates on its own schedule. A node or option available on one may not exist on the other yet, so test promotions across versions.</p>
</li>
<li><p>Activation state in JSON. Decide whether imports keep the exported active flag or always land inactive, and make it the same every time.</p>
</li>
<li><p>"Just one quick fix" in the UI. This is how the repo and production drift apart. Make the export step part of finishing any change.</p>
</li>
</ul>
<p>None of this needs heavy tooling. A repo, a normalize script, a written promotion checklist and basic metrics will keep a multi-instance n8n setup understandable even when one person runs all of it.</p>
<p>If you are untangling an n8n setup that grew faster than its process, you can reach me at <a href="https://faishalwahiduddin.com/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=blog-network-2026-10">https://faishalwahiduddin.com/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=blog-network-2026-10</a></p>
]]></content:encoded></item></channel></rss>