jackfranklyn
But I've noticed the products I actually keep coming back to are the ones that feel opinionated. They decided what they were and stuck with it. The ones that try to be everything usually end up being mediocre at all of it.
The WoW comparison in this thread is apt. The early expansions had clear identities. The later ones kept bolting on systems until playing felt like managing a spreadsheet.
The career incentive thing is spot on too. Nobody writes "migrated team from a complex internal system to a shared spreadsheet" on their CV. But "architected and shipped a real-time collaborative data platform" looks great, even if it does the same thing worse.
What's maddening about this whole situation though is that Google already has every signal it needs. The GitHub repo links to nanoclaw.dev. The npm package links to it. The commit history proves authorship. But apparently domain age and raw backlink count still trump verified ownership signals. The system rewards whoever stakes out the domain first, not whoever actually built the thing.
I've been using both approaches in projects and the pattern I've landed on: MCP for anything stateful (db connections, authenticated sessions, browser automation) and CLI for stateless operations where the output is predictable. The reason is simple - MCP tool definitions sit in context permanently, so you're paying tokens whether you use them or not. A CLI you can invoke on demand and forget.
The discovery aspect is underrated though. With MCP the model knows what tools exist and what arguments they take without you writing elaborate system prompts. With CLI the model either needs to already know the tool (grep, git, curl) or you end up describing it anyway, which is basically reinventing tool definitions.
Honestly the whole debate feels like REST vs GraphQL circa 2017. Both work, the answer depends on your constraints, and in two years we'll probably have something that obsoletes both.
The surrogate credentials + proxy approach mentioned above is probably the most robust pattern. Give the agent a token that maps to the real one at the boundary. That way even if the agent leaks it, the surrogate token is scoped and revocable.
For local dev with AI coding assistants, I've settled on just keeping the .env out of the project root entirely and loading from a path that's not in the working directory. Not bulletproof but it means the agent has to actively go looking rather than stumbling across it.
Two things that surprised us: (1) being explicit about what the tool doesn't do matters as much as what it does - vague descriptions get hallucinated calls constantly, and (2) inline examples in the description beat external documentation every time. The agent won't browse to your docs page.
The schema side matters too - clean parameter names, sensible defaults, clear required vs optional. It's basically UX design for machines rather than humans. Different models do have different calling patterns (Claude is more conservative, will ask before guessing; others just fire and hope) so your descriptions need to work for both styles.
What I do instead: monthly calendar reminder, run npm audit, update things that actually matter (security patches, breaking bugs), ignore patch bumps on stable deps. The goal isn't "every dep is always current" - it's "nothing in production has a known vulnerability". Very different targets.
Vibe coding would be catastrophic here. Not because the AI can't write the code - it usually can - but because the failure mode is invisible. A hallucinated edge case in a tax calculation doesn't throw an error. It just produces a slightly wrong number that gets posted to a real accounting platform and nobody notices until the accountant does their review.
Where I've found AI genuinely useful is as a sophisticated autocomplete. I write the architecture, define the interfaces, handle the domain logic myself. Then I'll use it to fill in boilerplate, write test scaffolding, or explore an API I'm not familiar with. The moment I hand it the steering wheel on anything domain-specific, things go sideways fast.
The article's point about understanding your codebase is spot on. When something breaks at 2am in production, "the AI wrote that part" isn't an answer. You need to be able to trace through the logic yourself.
My project has a C++ matching engine, Node.js orchestration, Python for ML inference, and a JS frontend. No LLM suggested that architecture - it came from hitting real bottlenecks. The LLMs helped write a lot of the implementation once I knew what shape it needed to be.
Where I've found AI most dangerous is the "dark flow" the article describes. I caught myself approving a generated function that looked correct but had a subtle fallback to rate-matching instead of explicit code mapping. Two different tax codes both had an effective rate of 0, so the rate-match picked the wrong one every time. That kind of domain bug won't get caught by an LLM because it doesn't understand your data model.
Architecture decisions and domain knowledge are still entirely on you. The typing is faster though.
I've been building tools that integrate with accounting platforms and the number of times a platform's API docs or published rate limits have simply disappeared between when I built something and when a user reports it broken is genuinely frustrating. You can't file a support ticket saying "your docs said X" when the docs no longer say anything because they've been restructured.
For compliance specifically - HMRC guidance in the UK changes constantly, and the old versions are often just gone. If you made a business decision based on published guidance that later changes, good luck proving what the guidance actually said at the time. The Wayback Machine has saved me more than once trying to verify what a platform's published API behaviour was supposed to be versus what it actually does.
The SOC 2 / audit trail point upthread is spot on. I'd add that for smaller businesses, it's not just formal compliance frameworks - it's basic record keeping. When your payment processor's fee schedule was a webpage instead of a PDF and that webpage no longer exists, you can't reconcile why your fees changed.
Before our tools: a bookkeeper spends 80% of their time on data entry and transaction categorisation, 20% on actually thinking about the numbers. After: those ratios flip. The bookkeeper is still there, still needed, but now they're doing the part that actually requires judgment.
The catch nobody talks about is the transition period. The people who were really good at the mechanical part (fast data entry, memorised category codes) suddenly find their competitive advantage has evaporated. And the people who were good at the thinking part but slow at data entry are suddenly the most valuable people in the room. That's a real disruption for real humans even if the total number of jobs stays roughly the same.
I think the "AI won't take your job" framing misses this nuance. It's not about headcount. It's about which specific skills get devalued and how quickly people can retool. In accounting at least, the answer is "slowly" because the profession moves at glacial speed.
I decided early not to patent anything. Not because our technical approach isn't novel - we have some genuinely interesting pattern matching pipelines - but because the defensibility comes from accumulated domain knowledge that's almost impossible to replicate quickly. We've hand-built logic for 16 different VAT code classifications, edge cases in how platforms handle API fields differently, quirks in how bank statement descriptions vary across hundreds of merchants. None of that is patentable. All of it is incredibly hard to reproduce.
A competitor could read a patent filing and build a workaround in months. They can't shortcut two years of discovering that one accounting platform silently ignores a field that every other platform respects, or that merchant descriptions follow completely different patterns at different transaction amounts.
The practical concern with provisionals is the 12-month clock. If you file one, you've committed to either converting to a full patent (expensive, time-consuming) or letting it lapse. At pre-seed, that's a decision you're forcing on your future self at potentially the worst possible time.
If your "core technical approaches" are truly about AI model architecture, that's a different conversation - but if it's mostly about how you apply models to a specific domain, I'd argue your time is better spent going deeper into the domain than drafting patent claims.
I build accounting automation software. The defensible part isn't the code architecture - it's years of accumulated domain knowledge. How different platforms handle VAT codes differently. How the same merchant shows up as fifteen different description strings on bank statements. What a "partial invoice payment with a currency adjustment and a bank fee" actually looks like when you need to post it correctly.
Open sourcing that would hand competitors the playbook without helping end users, because end users are bookkeepers, not developers. They don't want to read source code. They want to log in and have their transactions coded.
For developer tools the logic flips entirely - your users CAN evaluate and contribute to the source, and trust matters in a way that only auditability can satisfy (as another commenter noted about credentials). But the article's framework seems to assume your end user is technical. For vertical products where the complexity is domain knowledge rather than code patterns, staying closed is usually the right call.
General-purpose tools try to serve everyone and end up with features that kinda work for most cases. Someone deep in a niche builds for the edge cases they actually encounter, and it turns out those edge cases are universal within that niche - they just weren't visible to outsiders.
What actually happens is tiered understanding. I might vibe-code a utility function (don't care about implementation, just that it works), but I need to deeply understand data flow and business logic boundaries.
The 20k LOC weekend SaaS stories are real but missing context. Either the person has deep domain knowledge (knows WHAT to build, AI helps with HOW), or it's mostly boilerplate with thin business logic.
For complex systems, the testing point is key. AI generates code faster than you can verify behaviour. The bottleneck shifts from "writing code" to "specifying behaviour precisely enough that you know when it's right". That part isn't going away.
The people I see struggling aren't the ones who don't understand their code - it's the ones who don't understand their requirements.
I work with banking data day to day and the internal systems are often just as rough. CSV exports with inconsistent date formats between the same bank's own products. Transaction descriptions that are random truncated strings with no standardisation. Every bank formats their statements differently and some of them can't even stay consistent between their own account types.
You'd think with the regulatory pressure around data accuracy this stuff would be sorted by now. But the reality is most banks treat their digital infrastructure like legacy plumbing - it works well enough that nobody wants to risk touching it.
The flaky test problem usually comes from either race conditions (waiting for wrong things) or environmental differences. Adding AI vision on top often adds another layer of flakiness - now you're debugging "why did the model misread this button" on top of "why did the test timeout."
For mocking external services specifically - tools like MailHog (email) or mock OAuth providers tend to be more reliable than screenshot-based approaches. The determinism matters.
That said, if you genuinely need to test against production-like visual state - Playwright's screenshot comparison (toHaveScreenshot) combined with proper wait strategies has gotten pretty solid. The visual regression approach catches layout bugs that functional tests miss.
What surprised me was how much the ugly first version taught me that planning never could. You learn what users actually care about (often not what you expected), which edge cases matter in practice, and what "good enough" looks like in context.
The hardest part is giving yourself permission to ship something you know is flawed. But the feedback loop from real usage is worth more than weeks of hypothetical architecture debates.
The "ask for contrasting perspectives" prompt is annoying specifically because it makes you process more information. The devil's advocate approach forces a second round of evaluation. Even just opening a fresh session adds friction that makes you reconsider the question.
When I'm working in domains I know well, I catch the model drifting way faster than in areas where I'm learning. Which suggests the real problem isn't the model - it's that we're outsourcing judgment to it in areas where we shouldn't be.
The uncomfortable answer might be: if you're worried the model is reinforcing your biases, you probably don't know the domain well enough to evaluate its answers anyway.
The domain-specific wrinkle for accounting: every firm has their own coding style. What one bookkeeper calls 'Office Supplies' another calls 'General Admin'. So we had to build pattern learning at the individual level - watch how each person codes, learn their preferences, then apply that to new transactions.
The tax rules (VAT in the UK) add another layer - sometimes the same merchant gets different VAT treatment depending on what you bought. Gets messy fast.
Would be happy to swap notes - feel free to reach out. Always interested in how other domains handle the 'close enough to be useful, flexible enough for edge cases' problem.
If a function does actual computation or has branching logic that could go wrong in subtle ways, unit test it. If it's mostly wiring things together (fetch data, transform, save), an integration test catches more real bugs.
The "unit test everything" era came from a time when spinning up test databases was painful. Now with Docker and in-memory DBs, integration tests are often faster to write AND more useful.
Where unit tests still win: algorithmic code, parsing, anything with lots of edge cases. Writing unit tests forces you to think about boundaries. Integration tests just tell you "it worked this time."
The worst outcome is having tests that make refactoring painful but don't catch real bugs. I've seen codebases where every internal method signature change breaks 50 unit tests that were essentially testing implementation details.
What I find interesting is how these physical displays handle the inevitable "ghost trains" in the feed - cancelled services that still show as running, or trains that briefly appear in the wrong location. The software problem is messier than the hardware.
The trap is when correctness is subjective. Tone, phrasing, whether something 'sounds right' - no automated check helps there, so you're back to reviewing everything.
For structured data like invoices, I've found pattern-matching against known values beats LLMs anyway. Less hallucination risk, faster, and when it fails at least it fails obviously rather than confidently wrong.
Started with "configure your coding rules" - let users set up how transactions should be categorised. Made sense architecturally. Nobody used it. They'd stare at the config screen and give up.
What actually worked: learn from their existing data. Pull their historical GL, reverse-engineer how they already code things, then just do it that way. No configuration, no "agent" concept. Upload bank statement, get coded transactions back.
The insight that killed our complexity was realising bookkeepers don't think in rules. They think in outcomes. "This should be coded like the last time we saw a payment from Stripe" not "if merchant contains 'stripe' and amount > 100 then category = payment processing fees".
Curious about your MCP orchestration layer - how do you handle cases where the described workflow is ambiguous? In accounting there's often multiple valid ways to code something.
The fuzzy threshold question is tricky because false positives are worse than false negatives. A user seeing a wrong match erodes trust fast. We ended up with a tiered approach: high-confidence matches go through automatically, medium-confidence gets surfaced for human review, low-confidence stays unmatched rather than guessing.
One thing we found: the hardest cases aren't the ones where strings are slightly different - they're the ones where the same transaction appears with completely different descriptions on each side. "PAYPAL *ACME" vs "Invoice 1234 - Acme Ltd". No amount of fuzzy matching helps there. That's where learning from historical patterns (how did the user match these before?) beats trying to infer semantic similarity from scratch every time.
The cynical approach would be to make the product hard to leave. But that just means you've built a trap, not something people actually want. Eventually they escape and hate you for it.
The test I use: would people recommend this to colleagues even if there's no referral incentive? If the answer is no, I'm probably building something people tolerate rather than something they value.
Also resonates with the "product first" approach. Starting with a real problem you actually have - and then following where the tech decisions naturally lead - seems to produce better outcomes than starting with "I want to build something in X framework."
The QR sharing feature is a nice touch. Physical sharing had something going for it that we lost when everything went digital - that friction-free "here, try this" moment. Recreating that digitally is harder than it sounds.
The pattern that works best for me: I describe what I want at a high level, let it scaffold, then I read through and course-correct. The reading step is crucial. Blindly accepting generates technical debt faster than you can imagine.
Where it really shines is the tedious stuff - writing tests for edge cases, refactoring patterns across multiple files, generating boilerplate that follows existing conventions. Things that would take me 45 minutes of context-switching I can knock out in 5.
The automation piece I've landed on: I let it handle file operations and running commands, but I stay in the loop on architecture decisions. The moment you start rubber-stamping those, you end up with a codebase you don't understand.