How to build internal software that still works as the business grows
reading · 00%

How to build internal software that still works as the business grows

Architecture choices that keep a Retool app from needing a rebuild every two years.

Published
Reading6 min
CategoryRetool
Words1,263

The support dashboard was built in a fortnight and worked. Two years later every screen has its own copy of the customer lookup query, each slightly different. Nobody can say which one the refunds page uses. A new data source means touching every screen, so the team is talking about a rebuild.

Rebuilds like that rarely start with the platform. Retool supplies the components, the connections, permissions, environments, and source control. What decides whether an app survives two years of change is the architecture inside it: where the logic lives, how data moves, and what the app does when something fails. This guide covers the choices that keep an app changeable, with a SQL pattern and a checklist you can copy.

Put logic in one place

Business logic belongs in named queries, query library entries, or modules, not scattered across component event handlers. When a rule changes, you change it once.

A customer lookup is the usual first candidate. Build it once, as a shared query or a Retool module, and reuse it in the support dashboard, the sales tool, and the operations screen. A change to the lookup then reaches every screen that uses it, and no screen needs editing.

The test is simple. If a business rule appears in more than one place, it will drift. Find the copies and replace them with one source.

Make the data flow readable

Someone opening the app in six months should be able to trace where each number comes from without reverse-engineering it. That needs three habits:

  • Names that say what things are. customerLifetimeValue and getOpenTicketsForAccount, not query7 and transformer2.

  • One job per step. Fetch, then reshape, then display. Each step is a named query or transformer with one job.

  • Access decided in layers. The data connection sets what the app can reach. Permission groups set who sees each screen, and the query sets which rows come back.

Take a support dashboard that pulls tickets from Zendesk, enriches them with account data from Salesforce, and shows resolution times. With each step named and separate, a new ticket status or a second data source changes one step, not the whole screen.

Connect to other systems properly

Every integration needs proper authentication, explicit error handling, and respect for the other system's rate limits. A workaround that skips any of the three holds up in a demo and fails under real load.

Use the connection type built for the system where one exists: Postgres, Salesforce, HubSpot, Snowflake, or a REST API for an older ERP. Store credentials on the resource, not in the app. Give the resource the narrowest access that works, because every app that uses it inherits that access.

Build the foundations a prototype skips

Sign-in through the company's identity provider

Users sign in once through Okta, Microsoft Entra ID, or another SSO provider. Their groups in that directory map to Retool permission groups, so leaving the company removes access everywhere at once.

Separate environments

Development, staging, and production stay apart. Each environment points at its own data, and configuration that differs between them lives in environment settings, not in the app. Changes are tested on staging before anyone in operations sees them.

Errors with context

When a query fails, the log should say what the user was doing, which query failed, and which record it touched. An error that only says "something went wrong" turns every fix into guesswork. Route failures to a named owner, not to a shared inbox.

Multi-step work outside the screen

A workflow that calls several services belongs in its own layer, such as Retool Workflows or a service you own. A chain of button handlers is the wrong home. Employee onboarding is a good example: create the HR record, provision email, assign equipment, update the org chart. The orchestration layer runs the steps, retries or rolls back on failure, and reports status to the screen.

Data that grows without slowing down

Tables carry the right types, constraints, and indexes from the start. Queries paginate large result sets on the server, not in the browser. Components handle empty states and loading states, because both happen in production.

Here is the pattern for a large table, in Postgres, with keyset pagination:

SQLtickets_page.sql
-- One index that matches the filter and the sort order
CREATE INDEX IF NOT EXISTS tickets_account_created_idx
  ON tickets (account_id, created_at DESC, id DESC);

-- In Retool: one page at a time, from the last row the user saw
SELECT id, subject, status, created_at
FROM tickets
WHERE account_id = {{ accountSelect.value }}
  AND (
    {{ lastSeen.value == null }}  -- first page
    OR (created_at, id) < ({{ lastSeen.value?.created_at }}::timestamptz, {{ lastSeen.value?.id }}::bigint)
  )
ORDER BY created_at DESC, id DESC
LIMIT 50;

lastSeen is a Retool variable that holds the last row of the current page, and starts empty. Keyset pagination stays fast on page 200, where an OFFSET query scans every row it skips. More patterns are in slow tools are an architecture problem.

Fit the tool to how people work

The architecture can be right and the tool still go unused. Before the first screen, map how the work moves today: who does each step, what they check first, and where they reach for a spreadsheet.

A support team asking for "a better ticket dashboard" often needs faster access to customer context, not new charts. If the team enters data in spreadsheets today, a table-based form will feel familiar. If it coordinates in chat, send the tool's notifications there. Agree on a measurable target with the product owner, such as approval cycle time or the share of entries still typed by hand. Then everyone can see whether the tool works. The tool shipped. The team went back to Slack. covers this in depth.

Design for the next change

Every internal tool will change, as teams, policies, and connected systems change. Four habits keep the next change small:

  • Schemas that accept new fields and relations without restructuring production data.

  • Layouts built from containers and modules, so a new section does not disturb the rest.

  • Rules kept in identifiable places, so a policy change touches one query.

  • A short written record of what each query, module, and workflow is for.

The checklist

Copy this into the app's repository and review it before each release.

YAMLapp-architecture-checklist.yaml
logic:
  - every business rule lives in one named query, module, or workflow
  - no rule is duplicated across screens
data_flow:
  - queries and transformers have descriptive names
  - each step does one job: fetch, reshape, or display
  - access is set on the resource, the permission group, and the query
integrations:
  - credentials stored on the resource, narrowest access that works
  - errors handled explicitly, rate limits respected
foundations:
  - sign-in through the company identity provider, groups from the directory
  - separate development, staging, and production environments
  - failures logged with user action, query, and record, routed to a named owner
  - multi-step work runs in a workflow layer, not in button handlers
  - large tables indexed and paginated on the server
change:
  - schema accepts new fields without restructuring production data
  - purpose of each query, module, and workflow written down

Our view

We recommend deciding where logic lives and how data flows in sprint one, because both get harder to change once people rely on the app. The other foundations can arrive in stages, as long as each one lands before the team depends on it. The product owner decides the order. For the maintenance side of the same question, see the tool was free to build. Maintaining it is not.

Filed underRetoolProduction
Worth passing along? Share this note.
LinkedInX
Newsletter

Get the next one in your inbox

We write when something is worth reading. Subscribing means you are happy to hear from us when that happens. Leave any time. Privacy policy.

Founder

John Miniadis

Keep readingRetool
Related notes

Still here? Good sign

More notes from real builds, written up for the next person who needs one.

All articles
ContactLet's chat
Let's chat

Tell us what
you're building

Describe the process that is slowing your team down: the inbox, the spreadsheet, the portal nobody owns. We'll come back with a concrete approach, a rough timeline, and, where it helps, a working prototype within weeks.

What happens next
  1. We read every messageAn architect reads what you sent before anyone replies.
  2. You hear back in ≤ 2 daysA short reply with first questions and a suggested next step.
  3. We scope and prototypeA scoping call, then a working surface, not a slide deck.
Where are you in the process?