Ask any operator where their spreadsheet finally fell apart, and the answer is almost always the same moment: they needed one record to connect to many others, in both directions, without copying anything. A technician works many jobs. A job needs many technicians. A product ships with many features, and every feature lives in many products. The spreadsheet has no honest way to hold that, so you start duplicating rows, and the duplicates start disagreeing with each other. That is the exact problem relational data was invented to solve, and it is the exact problem most no-code tools quietly refuse to solve well.
This article is about the hardest relationship to model and the one you need most: many-to-many. Traditionally it means junction tables, foreign keys, and glue code. In Mulstra it means one action.
The three ways records relate
You only need three shapes to describe how almost any business connects its data. In plain language:
belongs_to — a record points to exactly one parent. An invoice belongs to one customer.
has_many — the flip side. A customer has many invoices. Same link, read from the other end.
many_to_many — both sides can have many of the other. A product has many features; a feature belongs to many products. Neither side owns the other.
The first two are easy enough that even a spreadsheet fakes them with a column of IDs. Many-to-many is where the fakery collapses. There is no single column that can hold "this feature appears in these seven products" while those products each hold "this product includes these twelve features" and keep both lists in sync when either changes.
Why the spreadsheet always loses here
In a spreadsheet, a link is just text you typed into a cell. Nothing enforces it. When a feature belongs to many products, you have two bad options. You duplicate the feature row once per product, and now editing its description means editing it in every copy. Or you cram a comma-separated list of product names into one cell, and now you cannot filter, count, or click through to anything.
The moment your data needs to be read from both directions, a flat file stops being a tool and starts being a liability.
Generic no-code builders improve on this but rarely finish the job. Many let you link a record to one other record, or to many in a single direction, then leave you to maintain the reverse side by hand. You end up rebuilding, badly, the thing databases already do. If you have felt this exact wall, the piece on going from spreadsheet to system covers the broader shift in mindset.
What the database way actually costs
A real database models many-to-many with a junction table (also called a join table): a third, hidden table whose only job is to hold pairs. Product 4 links to Feature 9. Product 4 links to Feature 12. Feature 9 links to Product 7. Every relationship is a row in this middle table.
It works, and it is correct, but it is not free. Someone has to design that table, keep its foreign keys valid, write the queries that join across it, and update it whenever a link is added or removed. That is backend work. It is exactly the work an operator running a 20-person company should never have to think about, and the reason "just use a database" is not a real answer for most teams.
In Mulstra it's one action
Mulstra keeps the correctness of the database and throws away the labor. You define a relation between two objects once, and the platform maintains the junction, both directions, and the navigation for you. Conceptually, the entire setup is a single declaration:
relation Product <-> Feature (many_to_many)That one line is the whole junction table, both foreign keys, and the reverse lookup. From then on:
Open any Product record and you see its Features, editable inline.
Open any Feature record and you see every Product it belongs to, already there.
Add or remove a link from either side, and the other side updates instantly.
No third table to name. No IDs to copy. No glue code to keep two lists agreeing. You relate the objects, and both records stay linked and navigable automatically.
Why this matters on an ordinary Tuesday
The payoff is not architectural elegance. It is that the answers your team needs are one click away, from whichever record they happen to be looking at.
A security guard assigned to many client sites, and each site covered by many guards. Open the guard, see every site and shift; open the site, see every guard who has worked it. That two-way view is the backbone of guard management.
A technician assigned to many jobs, and each job needing many technicians, parts, and visits. Pull up the technician for their full history, or the job for everyone on it, without rebuilding the link each way. This is the core of field service operations.
A tenant with many leases across units and years, and each unit tied to many tenants over its life. A unit's full maintenance and occupancy history stays intact no matter who is renting it today.
A customer's complete history — every order, ticket, and conversation — reachable from the customer, and every order still reachable from its product.
In each case the same truth holds: the relationship is real, it runs both ways, and nobody had to build a junction table to make it so.
The takeaway
Many-to-many is not an advanced feature you graduate to. It is the shape of ordinary business — people, places, and things that connect to many other people, places, and things at once. The only question is whether your tools model that honestly or make you fake it with duplicated rows and manual lists. Mulstra makes it a single action, keeps both sides in sync, and lets you navigate from either end. Define the relation once, and let the data stay related.