Skip to content

From AppSheet to Production: How Claude Code Built a Full Enterprise Platform

TL;DR

We replaced a no-code AppSheet app with a custom Next.js platform for a European textile manufacturer — handling 32-size-column order management, deterministic PDF import, and barcode label generation. Claude Code acted as a senior engineering partner, compressing what would have been a 3–6 month agency project into a fraction of that timeline and cost.

In a previous article, we wrote about connecting a German textile manufacturer's systems with MCP — giving their team a single AI-powered interface to query across ERP exports, spreadsheets, and email. That project demonstrated how MCP can unify fragmented data sources without centralizing everything into one monolith.

This is about what came next.

The same manufacturer had been running their order management and label printing workflow on Google AppSheet for several years. AppSheet is a capable no-code platform for straightforward CRUD applications, but it started hitting hard limits as the business grew.

Where AppSheet Fell Short

The core challenge was domain complexity. This company manages production across external factories and suppliers, coordinating orders that each contain multiple production lots, each broken down across 32 clothing size columns — from size 23 through size 122. AppSheet could handle basic data entry, but three requirements pushed it past its limits.

Deterministic PDF import. Suppliers send order confirmations as PDFs with specific positional layouts. The team was manually retyping quantities from these PDFs into the system — hundreds of cells per order, across dozens of sizes. This was slow, error-prone, and expensive.

Complex label generation. Each lot needs barcodes and packing labels with precise formatting, auto-hidden empty size columns, and support for variable lot structures. AppSheet's printing capabilities could not handle this.

Role-based multi-tenant access. Different supplier companies needed access to their own orders only, with admin users having full visibility. AppSheet's permission model was too coarse for this requirement.

A SaaS product might have addressed some of these needs, but the size-column structure is highly specific to this manufacturer's workflow. No off-the-shelf solution maps 32 size columns cleanly.

Why Claude Code, Not Another No-Code Tool

The requirements were too domain-specific for another no-code platform, but building from scratch in the traditional way — scoping, design, development, testing — would have been prohibitively expensive for a company of this size.

Claude Code changed the economics. Instead of hiring a development team for several months, we could work as a small team — one developer with Claude Code functioning as a senior engineering partner that understands the full codebase context.

The key difference from using AI for code snippets or autocomplete is codebase-level reasoning. Claude Code reads the entire project — schema, actions, components, tests, deployment config — and makes changes that are consistent with the existing architecture. When we describe a feature in natural language, the implementation respects the patterns already established in the codebase.

There is a growing category of AI automation that focuses on stitching together pre-built workflow blocks — tools like N8N and Make. These are useful for standard integrations. But when the domain is specific enough that no pre-built block exists — 32 clothing sizes, positional PDF extraction, SCADA tag management — you need someone who can build the block. That is what we do.

Architecture

We chose a stack optimized for simplicity and operational cost:

┌──────────────────────────────────────────┐
│           Next.js 15 (App Router)         │
│   React 19 + Tailwind CSS + Heroicons     │
├──────────────────────────────────────────┤
│         Server Actions (11 modules)       │
│    Auth guards, activity logging, CRUD    │
├──────────────────────────────────────────┤
│      NextAuth v5 (JWT + Credentials)      │
│    Role-based: admin | user + companyId   │
├──────────────────────────────────────────┤
│           SQLite (single file DB)         │
│     WAL mode for concurrent access        │
├──────────────────────────────────────────┤
│      Docker (multi-stage, standalone)     │
│    Named volume for data persistence      │
└──────────────────────────────────────────┘

Why SQLite. The application runs on a single VPS. SQLite in WAL mode handles concurrent reads and writes well for this scale (dozens of users, not thousands). It requires no external database server, backs up as a single file copy, and the operational simplicity is significant — there is no database container to manage, no connection pooling to configure, no credentials to rotate.

Why standalone output. Next.js 15's standalone mode produces a self-contained server that only includes the files needed for production. The Docker image copies just the built output, not the source code or node_modules, resulting in a small, fast container.

Why Server Actions over REST. All data access flows through 11 server action modules — no REST API layer needed. Each action starts by calling requireAuth() or requireAdmin(), keeping authorization logic co-located with the data access code.

The AI-Powered Features

"AI-powered" is an overloaded term. In this project, the features that matter most are not powered by AI at runtime — they are powered by deterministic code that was written with the help of AI.

Deterministic PDF Import

This is the headline feature. The PDF extraction engine is 444 lines of TypeScript that uses pdfjs-dist to extract text items with their x/y coordinates, groups them into rows by vertical position, identifies the header row containing size columns, and maps each data cell to the correct size column based on horizontal position.

No LLM involved

There is no LLM in the extraction pipeline. It is purely positional matching — instant, free, and deterministic.

  1. Extract text items with coordinates from each PDF page
  2. Group items by y-position (3px tolerance) to form rows
  3. Find the header row by detecting 3+ measurement values (23-122)
  4. Build a column map from header x-positions
  5. For each data row, match numeric cells to columns by x-proximity
  6. Merge results across pages, normalizing lot names and totals

The result handles multi-page PDFs with variable lot counts, and the same PDF always produces the same result.

After extraction, the user reviews the data in an editable table with all 32 size columns, can shift quantities left or right across size columns (to correct column alignment from extraction), and confirms before import. The system validates extracted stock quantities against computed values and flags any mismatches.

Barcode and Packing Labels

Each lot gets a unique barcode. Packing lists are generated with a layout that automatically hides size columns with zero quantities — since most orders only use a subset of the 32 possible sizes, this prevents printing pages full of empty columns.

The packing algorithm is overridable: administrators can manually adjust the generated packing layout for special cases, and these overrides persist across recalculations.

CSV Sync from Google Sheets

The manufacturer still uses Google Sheets for some data entry workflows. Rather than forcing a complete process change, we built a sync bridge. A sync action fetches three CSV exports from published Google Sheets using PapaParse for robust parsing, wraps the entire operation in a database transaction (clear and rebuild), and preserves company-to-order assignments across syncs. If anything fails, the transaction rolls back and the existing data is untouched. The action returns statistics: orders processed, lots created, produced lots synced.

This kind of pragmatic integration — bridging legacy workflows instead of replacing them overnight — is where custom platforms have a clear advantage over SaaS products. Not many off-the-shelf tools would support this specific sync pattern.

Activity Logging

Every significant action — order creation, lot modification, PDF import, user management — is logged with a fail-safe wrapper — if logging fails, it never breaks the main operation. Each log entry captures the user's identity, action type, entity reference, and details. Administrators can search and filter the full audit trail from the admin panel.

The Build Process — What We Learned

This is the section that matters most if you are considering using Claude Code for a similar project.

The CLAUDE.md Pattern

Every project we build with Claude Code starts with a CLAUDE.md file in the repository root. This file is the AI's briefing document — it describes the project structure, architecture decisions, naming conventions, and constraints. Claude Code reads it automatically and uses it as context for every interaction.

For this project, the CLAUDE.md covered:

  • Tech stack and versions — Next.js 15, React 19, SQLite with Drizzle, NextAuth v5 beta
  • Database schema — core tables with their relationships and the 32 size columns
  • Server action patterns — auth guards, error handling, the convention of co-locating authorization with data access
  • Component structure — which components handle which responsibilities
  • Deployment — Docker multi-stage build, named volumes, environment variables

TIP

This upfront investment pays off enormously. Instead of explaining context in every conversation, Claude Code already understands the codebase and makes changes that fit. The CLAUDE.md does not need to be perfect from day one — it evolves with the project.

What Claude Code Excels At

Schema-to-UI generation. Given a Drizzle schema with 32 size columns, Claude Code generates the corresponding form components, validation logic, and display tables correctly. Writing 32 form inputs with proper labels, validation, and state management by hand is tedious; Claude Code handles it in seconds.

Server action boilerplate. The pattern of requireAuth() → query → return result is consistent across all 11 action modules. Once the pattern was established in the first module, Claude Code replicated it faithfully across the rest, including proper error handling and TypeScript types.

Cross-file consistency. When we added a new field to the schema, Claude Code updated the schema file, the relevant server actions, the form components, and the display components in one pass. Keeping all these files in sync manually is where bugs typically hide.

Rapid iteration on feedback. The client provided feedback in Dutch. We could describe what needed to change in natural language, and Claude Code implemented it in minutes. A request like "the packing list should hide columns where all lots have zero for that size" went from description to working implementation in a single interaction.

What Needs Human Judgment

Architecture decisions

Claude Code does not choose your stack. The decision to use SQLite over Postgres, Server Actions over REST, or standalone Docker output over a traditional deployment — these are engineering decisions that require understanding the operational context.

Domain logic validation

The PDF extraction logic works correctly, but verifying that the positional mapping produces the right results for every supplier's PDF format requires domain knowledge. Claude Code can write the code; a human needs to validate it against real documents.

UX flow design

Claude Code builds whatever UI you describe, but deciding the right user flow — import → review → edit → confirm → save — requires understanding how the end users actually work.

Deployment and security

Setting up the Docker pipeline, configuring environment variables, managing secrets, establishing dev/acc/prod environments — these operational concerns require deliberate human attention. Claude Code writes the Dockerfile; a human verifies it is secure.

Edge cases in business logic

The packing algorithm needs to handle special cases — orders with unusually large quantities, lots that span non-contiguous size ranges, split shipments. These edge cases are difficult to specify in natural language because they often only surface when processing real production data. The initial implementation works for the common case; the edge cases require iterative refinement with actual users.

Development Timeline

The platform went from zero to production-ready in a fraction of the time a traditional development process would have required. The CLAUDE.md file, once established, made each subsequent feature faster to implement because Claude Code accumulated context about the project's conventions and constraints.

For recurring tasks, the SFLOW Runner could schedule automated operations for this client — syncing data, generating reports, or monitoring system health — extending the platform's value beyond what was built manually.

Takeaways for Business Owners

Velocity, not replacement. Claude Code did not replace engineering judgment. It eliminated the mechanical work — the boilerplate, the repetitive patterns, the cross-file consistency — so that we could focus on architecture, domain logic, and user experience.

When to use this approach. This works well when you have a clear problem, domain-specific requirements that no SaaS product addresses, and someone who can make architecture decisions and validate outputs. It does not work if you have no technical oversight at all.

Cost efficiency. A comparable project scoped through a traditional agency would have been quoted at 3–6 months of full-time development. Claude Code compressed the development cost to a point where a custom platform became viable for a company of this size.

Invest in testing and deployment

This is amplified when using AI to build your application. Dev, staging, and production environments are not optional. Backups are not optional. Testing every feature against real data is not optional. The speed of AI-assisted development makes it tempting to skip these — do not.

Want to discuss a project like this?

SFLOW helps companies implement practical AI solutions — from custom platforms to industrial integrations with your existing systems.

SFLOW BV - Polderstraat 37, 2491 Balen-Olmen