İçeriğe geç
wedevit

November 2, 2026 · 9 min read · software

İlhan Buğra Aslan

Your ops team files a ticket for every data fix: how to design internal admin tools


When your operations team needs a developer for every record correction, the thing you are missing is not an admin panel. It is that those corrections were never defined as operations inside the product. An internal tool is not a patch bolted onto the side of the system, it is the product's second face, and it has to run through the same business rules, the same validation and the same side effects. Three decisions settle most of the design. Every action goes through the product's own service layer rather than straight into the database. Every action is recorded with who did it, what changed and why. Permissions are split along the real shape of the work instead of a single "admin" role, with deletions, refunds and identity changes gated behind time-limited elevation and a second approval. What remains is choosing which operations belong in the tool at all.

Start by counting the requests

Before anyone argues about build or buy, list every correction request that went from operations to engineering in the last thirty days. Three columns: what was asked, how long it took, how many times the same thing came back. Most lists look alike. Change a customer's email address, cancel an order, regenerate an invoice, fix a wrongly entered amount, move a user to a different account.

The decision rule comes from the repeat count. Anything recurring more than twice a month is no longer an exception, it is an undefined workflow, and it belongs in the tool. Things that happen once or twice a year can stay with a developer, as long as there is a written procedure for them. Teams that skip the counting step tend to ship forty screens nobody opens.

Why running SQL by hand is the expensive option

An update written directly against the production database skips every protection the application has. Validation does not run, state transitions that should be impossible become possible, and the side effects never fire.

Take a one-line query that flips an order's status to cancelled. The customer never gets the cancellation notice, stock is not returned, no refund record is created, the outbound webhook never fires, the ERP side keeps the order open, and cached copies of the old record keep getting served for a while. Fixes that touch several tables add their own failure class, where half the change lands and half does not.

Then there is the trail. Six months later, "who changed this record and why" has no answer. All that exists is a query somebody ran once.

The tool has to go through the same door as the product

The working arrangement is simple to state: the admin tool is a separate interface, not separate logic. "Cancel order" is defined in exactly one place. The same code runs whether the customer does it or a support agent does it on their behalf. The only difference is who initiated it and under what permission.

In practice that means every admin operation is called with three things: the operator, the affected record and a reason. The moment you open a second code path, bug fixes split in two as well, and a hole you closed on the customer side stays open on the internal side. The same holds for error handling and for concurrent edit protection.

Framework admin, internal tool platform, or your own

Three options, and all three can be right.

First, the admin interface your framework ships. Django's documentation is unusually honest about where it stops: the recommended use is limited to "an organization's internal management tool", and it is "not intended for building your entire front end around". The same page says that when you need a process-centric interface rather than a table-centric one, it is probably time to write your own views (Django admin docs).

Second, internal tool platforms. You start fast, but the governance features an admin tool actually needs tend to live on the upper plans. On Retool's published pricing, the free plan caps at 5 users; Team is $10 per builder and $5 per internal user per month billed annually; Business is $50 and $15. Audit logs arrive with Business, while granular roles, SSO/SCIM/2FA and self-hosting are Enterprise only. In other words, the answer to "who did what in the panel" is not available on the cheap plan.

If you plan to self-host an open-source admin platform, read the license before the docs. Directus moved to the Monospace Sustainable Core License with v12; organizations under $5M in annual revenue and 50 employees can use the full platform for free (license announcement). Above those thresholds it becomes a purchase line (open-source license compliance).

Third, building it yourself. When the actions are business operations rather than plain record editing (refunds, cancellations, account merges, plan changes) and the rules for those operations already exist in your product, writing the tool as part of the product usually costs less and leaks less. The test from when low-code stops being enough applies here too: look at the expressiveness ceiling and the exit cost.

One "admin" role is not a permission model

The New York financial regulator's report on the 2020 Twitter incident is worth reading on exactly this point. Twitter did limit access to its internal tools, but according to the report more than 1,000 employees still had it. Those tools could update an account's email address, reset passwords, and turn multi-factor authentication on or off. The attackers did not use a technical flaw; they phoned employees pretending to be the IT desk and took over their accounts (NYDFS Twitter Investigation Report, October 2020).

The lesson lands on role design. If the panel has a single permission level, the access a support agent gets to fix an address is the same access that resets passwords and disables MFA. Split roles by the work: read-only lookup, limited correction, financial action, destructive action. Add record-level scope on top, so an agent sees only the customers in their own book, which in most products is a few lines of condition (roles, permissions, RBAC and ABAC).

Second signature and time-boxed elevation

The same report makes a concrete recommendation: for high-risk functions, require certification or approval from a second employee before the action can be taken. That control caps the damage when one employee's account is compromised.

For time-boxed access there is a model worth copying. In Google Cloud's Privileged Access Manager, an administrator defines an entitlement: who may request access, which role they receive, for how long at most, whether a justification is mandatory, and whether approval is required. When the grant expires the roles are revoked automatically, and requests and approvals are written to audit logs (PAM overview).

The cheap version inside your own tool: destructive actions are off by default, the operator requests access with a written reason, the grant lasts 60 minutes, the request posts to a team channel, and the permission drops on its own when the clock runs out. If you are deciding how elaborate the approval path should get, the three-level test in hardcoded approval flows versus a workflow engine will settle it.

Rules for impersonation

Letting support see what the customer sees is a legitimate feature in most products. What makes it dangerous is when it quietly becomes an unlimited login.

A rule set that holds up: the session is read-only by default and acting on the account requires a separate permission. A banner stays on screen for the whole session. The session is time-limited and can be ended with one click. Every action is logged against both the operator and the customer. Password changes, email changes, disabling MFA and adding payment methods are forbidden inside an impersonated session. Product analytics and marketing events exclude these sessions, otherwise your funnel numbers quietly absorb support traffic.

Visibility deserves its own rules. Card numbers, national IDs and similar fields stay masked by default, unmasking asks for a reason, and export runs behind a separate permission with a row limit. In 2020 Shopify disclosed that two members of its support team had accessed customer records belonging to fewer than 200 merchants without authorization, and stated the incident was not caused by a technical vulnerability in the platform (Infosecurity Magazine, September 2020). The question to answer is not who can log in, it is how much a person who logs in can see and how much of it they can take out.

Bulk actions: preview, cap, undo

Bulk operations are where an internal tool does the most damage. Four controls are not optional.

Preview: before anything runs, show how many records will be affected and what the first few will look like. Cap: put a fixed ceiling on rows per run, with anything larger requiring separate approval. Durable execution: queue the job, show progress, and report partial failures row by row (background jobs and queues, bulk imports). Undo: give every bulk run a batch id so it can be reversed in one move, and mark records with soft delete instead of removing them. For operations that are hard to reverse, such as merging duplicates, keep a copy of both sides before the merge (finding and merging duplicate records).

And then there is the double click. Generate a request key for each bulk run and ignore the second request that arrives with the same key.

Every action carries a reason

The audit record for an internal tool should be more detailed than the product's general audit trail. Each row wants: timestamp, the real person who acted (not a shared account), the affected record, old and new values, a reason or ticket reference, the channel the action came through, and the source IP. The application should not be able to rewrite those rows afterwards.

Even if you never touch card data, two clauses of PCI DSS v4.0.1 make a reasonable benchmark: 10.2.1.2 requires logging all actions taken by anyone with administrative access, and 7.2.4 requires user accounts and their privileges to be reviewed at least once every six months. In practice that means opening the panel's user list with the head of operations once a quarter, and closing access on the day someone leaves. Automatic deprovisioning through your identity provider is more reliable than remembering to do it (SAML, OIDC and SCIM).

The panel is an attack surface too

Hiding the URL is not a control. Authorization has to be checked server-side on every request, and broken access control stays near the top of the OWASP list.

Three more things. Use phishing-resistant authentication for panel logins. The NYDFS report notes that application-based MFA was defeated by a phone call and that a hardware security key would have stopped the attack (MFA methods compared). Keep the panel session separate from the product session and keep it short. Monitor panel traffic on its own: an agent opening 400 customer records in an hour breaks no rule and will only be caught by monitoring (log management, SIEM and MDR).

Four things to do this month

Pull the last 30 days of manual fixes and sort them by repeat count. Implement the top three as real operations, with one entry point, a mandatory reason and an audit record. Split roles at least into view and act. Open the panel's user list today and remove the people who left and the people who no longer do that job.

If your operations team still gets corrections done by filing tickets, we can work out together which actions belong in a tool and what it costs to expose them safely.


Need help with this topic?

get in touch →← all posts