FileMaker data modeldatabase designFileMaker maatwerkschema refactoringrelational database best practicesFileMaker performance
How to design a maintainable FileMaker data model

How to design a maintainable FileMaker data model

Jeroen·

Learn how to structure a FileMaker data model that stays fast, flexible, and easy to maintain as your business and your app keep growing.

Your FileMaker system worked great in year one. Now, five years and a dozen ad-hoc changes later, every new feature request from sales or warehouse staff means digging through fields nobody remembers the purpose of, tables with cryptic names like tbl_temp2, and scripts that touch the same record from six different directions. Adding a single new field triggers a chain reaction of broken layouts and mystery calculation errors. This is not a FileMaker problem — it's a data model problem, and it happens to Access, ERP, and custom web apps just as often.

This article walks through what actually makes a FileMaker data model maintainable over years of real business change, not just on the day it launches.

What does "maintainable" actually mean for a data model?

A maintainable data model is one where a developer who didn't build it can still safely change it. Concretely, that means:

  • A new table can be added without renaming or restructuring existing ones.
  • A field's purpose is obvious from its name and table, without needing to ask the original developer.
  • Business rules live in as few places as possible, so a policy change (e.g. "discounts now require manager approval") doesn't need to be re-implemented in five scripts.
  • Reports and integrations can pull data without needing to know which of three overlapping fields is the "real" one.

If your team dreads opening the relationship graph, that's a strong signal the model has drifted from these principles.

Why do FileMaker data models degrade over time?

FileMaker's flexibility is exactly why this happens. Because a business owner or in-house developer can add a field or table in minutes without a formal migration process, small shortcuts accumulate:

  • A field gets repurposed. Notes1 was meant for delivery notes, but two years later someone starts using it for complaint tracking because it was "already there and empty enough."
  • Duplicate data creeps in. Customer email exists in the Contacts table and again in a CustomerEmail field on the Invoices table, copied by a script — and now they silently diverge when someone updates one but not the other.
  • Calculations replace real logic. A stored text field concatenates status flags instead of a proper related table, because it was faster to build under deadline pressure.
  • One table does three jobs. An Orders table also holds quote data and RMA (return) records, distinguished only by a Type field that not everyone filters correctly.

None of these are due to bad developers — they're the natural result of iterative development without a periodic pass to clean up the model. The parent guide on improving FileMaker performance and structure covers the broader set of symptoms this causes, from slow finds to layout sprawl; this article focuses specifically on the data layer underneath it.

What are the core principles of a maintainable FileMaker data model?

1. One table, one real-world entity

Each table should represent exactly one thing: a Customer, an Order, an Order Line Item, a Product, a Shipment. If you find yourself adding a field that only applies to some records in a table (a RMA_Reason field that's empty on 95% of Order records), that's a sign a separate table is needed — for example an OrderReturns table related back to Orders.

A practical test: if you can't name a table with a single noun without an "or" or "and" in it, it's probably doing two jobs.

2. Normalize first, denormalize only on purpose

Store each fact once. A customer's address belongs in the Customer table, not copied into every Invoice — instead, invoices relate to the customer and pull the address through a relationship or a snapshot field created intentionally at invoice time (because you do want to preserve what the address was when the invoice was sent, even if the customer moves later).

That last point matters: denormalization isn't always wrong. Storing a snapshot of price, address, or tax rate at the moment of a transaction is often the correct design, because business history shouldn't change retroactively when a customer record is edited. The rule isn't "never duplicate data" — it's "never duplicate data by accident."

3. Use dedicated join tables for many-to-many relationships

A classic case: Products can appear on many Orders, and Orders contain many Products. Trying to model this with repeating fields or comma-separated ID lists in a single field looks like a shortcut but makes reporting, searching, and integrations painful almost immediately. A proper OrderLineItems join table — with its own primary key, a foreign key to Orders, a foreign key to Products, quantity, and price-at-time-of-sale — solves this cleanly and is also where you capture that all-important price snapshot from principle #2.

[[IMAGE:left|Diagram of Orders and Products tables connected through a join table]]

4. Give every table a stable, unique primary key

Use an auto-entered, unguessable ID (a UUID or auto-incrementing serial) as the primary key — never a "natural" value like an order number, email address, or product SKU. Business identifiers change (a customer merges two accounts, a SKU gets renumbered after a supplier switch); a primary key must never need to change, or every related record referencing it breaks silently.

5. Name things so a stranger understands them

cust_id, CustomerID, and Customer::ID might all mean different things across a mismanaged file. Pick one naming convention — table prefixes, camelCase or snake_case, singular vs plural table names — write it down, and apply it consistently. This single habit saves hours every time a new developer (or an AI coding assistant) needs to work in the file.

6. Keep business logic close to the data, not scattered across scripts

When a rule like "an order can't be marked shipped until it's fully paid" is enforced only inside a button script, it's trivially bypassed by a different script, an import, or a future developer who didn't know it existed. Where possible, enforce such rules with validation calculations on the field or table itself, or centralize them in a single subscript every write path calls — not duplicated inline five times.

7. Separate operational data from reference/config data

Lookup tables — statuses, categories, tax rates, unit types — should live in their own small tables rather than as hardcoded value lists baked into field definitions. Value lists based on custom values seem convenient until the business adds a ninth order status and someone has to remember every layout, script, and report where the old list of eight was hardcoded.

How do you refactor a data model that's already messy?

You rarely get to start over, and starting over is usually the wrong call anyway — the existing file holds years of business logic and data integrity that a rewrite risks losing. Instead:

  1. Document what exists first. Map every table, its real purpose, and any field that's overloaded or duplicated. FileMaker's Database Design Report (Tools > Database Design Report) is a good starting inventory.
  2. Identify the highest-pain tables, not all of them. Usually it's the one everyone complains about — often Orders, Customers, or Inventory.
  3. Add new tables alongside old ones rather than deleting fields immediately. Build the new OrderLineItems table, migrate data into it with a script, and validate it in parallel with the old structure before retiring old fields.
  4. Migrate scripts and layouts incrementally, table by table, testing each before moving to the next — never all at once.
  5. Retire old fields only after a full business cycle has run cleanly on the new structure (e.g. one full month-end close, one full order-to-cash cycle), so you catch anything the migration missed.
  6. Re-run reports and exports against both structures for a transition period to confirm the new model produces identical totals before anyone trusts it exclusively.

[[IMAGE:right|Before and after diagram of a messy table becoming three clean related tables]]

How does a clean data model affect performance?

A well-normalized model with proper indexing and correctly typed keys is almost always faster, not slower, than a flat, denormalized one — a common misconception. Finds run against indexed, single-purpose fields instead of parsing concatenated text fields. Relationships resolve predictably instead of joining on mismatched data types (a classic, easy-to-miss bug: relating a number field to a text field that looks numeric, which silently fails to match in some contexts). If performance is your primary concern right now rather than long-term maintainability, the broader techniques for that are covered in the parent article on FileMaker performance and structure.

Does a maintainable data model matter more once you add AI or integrations?

Yes — arguably more than for human users. When you connect FileMaker to an external system via API (say, syncing orders to an accounting package, or feeding inventory data to a webshop), the integration script depends entirely on field names and structure staying predictable. A field silently repurposed for a new meaning breaks the integration in a way that's hard to trace, because the API call still "succeeds" — it just sends wrong data.

The same is true when adding AI-assisted tools inside FileMaker — for example, natural-language reporting or an AI assistant that helps staff query data conversationally. An AI model answering "which customers haven't ordered in 90 days" is only as reliable as the underlying schema: if "customer" data is split across three overlapping tables, the AI (like a new human developer) will guess wrong more often. A clean, well-named, single-source-of-truth model is what makes both integrations and AI features trustworthy rather than a source of quietly bad answers.

Checklist: is your FileMaker data model maintainable?

  • Every table represents exactly one real-world entity
  • Every table has a stable, auto-generated primary key (not a business number)
  • Many-to-many relationships use dedicated join tables, not repeating fields or ID lists
  • No field is used for two different purposes depending on context
  • Naming conventions are documented and consistently applied
  • Business rules are enforced in one place, not copy-pasted across scripts
  • Reference/lookup data (statuses, categories, rates) lives in its own tables
  • A new developer could explain what each table does just from its name and fields
  • Reports and integrations pull from one clear source of truth per data point

FAQ

How many tables is "too many" in a FileMaker solution? There's no fixed number — some healthy solutions have 20 tables, others have 150. The question isn't count, it's clarity: can every table's purpose be explained in one sentence?

Should we redesign the data model before or after connecting an API integration? Before, if possible. Building an integration on top of a shaky model just automates the mess faster and makes it harder to unwind later.

Is it worth hiring a specialist just to review the data model, without a full redesign? Often yes. A focused schema review from someone experienced in FileMaker maatwerk can flag the two or three structural issues causing 80% of your maintenance pain, long before a full rebuild is justified.

Can we fix this ourselves gradually, or do we need a big-bang rewrite? Gradual, table-by-table refactoring (as described above) is almost always safer and cheaper than a rewrite, and it lets the business keep running on the system throughout.

A maintainable data model isn't a one-time design exercise — it's an ongoing discipline that pays off every time the business changes, adds a new sales channel, or wants a new report. If your FileMaker system has reached the point where every change feels riskier than it should, Loggix can help with a structured schema review, a phased refactor of the tables causing the most pain, or building the API integrations and AI-assisted tools on top of a foundation solid enough to trust.