9 min readJohnny UnarJohnny Unar

Ship one correct admin workflow

Config panels feel like product progress until they turn your SMB app into a support machine. Pick one strong admin path and defend it.

config is a tax

Every B2B SMB product starts with the same little compromise. A client wants invoice approval to work slightly differently, or they need one extra status between "draft" and "sent", or they insist their account managers should see one tab that nobody else sees, and the team says yes because it feels cheaper than arguing, cheaper than forcing a process change, cheaper than risking a deal. Six months later you have a Settings page with 43 toggles, three nested accordions, four levels of role overrides, and a test suite that passes only when nobody touches anything.

I've seen this pattern in ERP and CRM work over and over, including projects we build at Steezr for startups and SMBs, especially customer portals where admins are expected to "configure the system" instead of operate it. That phrase should make you nervous. Most of the time it means the product team gave up on making hard decisions, and engineering is left encoding every customer's local politics into a database schema.

Configurability has a carrying cost, and teams consistently underprice it. Every new option expands the state space, every conditional branch leaks into permissions, UI copy, exports, onboarding flows, docs, seed data, QA plans, and support scripts. You don't add one checkbox. You add another universe your software now has to respect forever. The ugly part is that customers rarely experience this as power. They experience it as uncertainty. They don't know which settings matter, which combinations are supported, why enabling one flag made a report disappear, or why the same workflow behaves differently across two tenants.

One correct admin workflow beats twenty configurable half-workflows. A sharp default with sane constraints ships faster, trains faster, breaks less, and gives support something they can actually reason about. Most SMB customers don't need a workflow engine. They need software that behaves predictably on a Tuesday afternoon when the office manager is trying to close invoices before lunch.

the test matrix lies

The usual defense of heavy configuration is that "the core stays the same" and only the surface changes. That's fantasy. Once settings affect permissions, automation, or state transitions, the core is gone. You've built a product generator.

Take a pretty ordinary Django admin backend with tenant-scoped settings stored in PostgreSQL JSONB, something like account.settings -> 'approvals' -> 'require_second_signoff'. Fine at first. Then another customer wants signoff only above amount > 5000, another one wants different thresholds by department, another one wants managers exempt, another one wants notifications via email and Slack, another one doesn't want approval to block PDF generation because their finance team "works around it manually". Soon your once-clear service method turns into this kind of mess:

python
if settings.require_second_signoff:
    if invoice.total_cents >= settings.signoff_threshold_cents:
        if settings.exempt_manager_role and user.role == "manager":
            pass
        elif settings.department_thresholds_enabled:
            threshold = settings.department_thresholds.get(invoice.department_id)
            if threshold and invoice.total_cents >= threshold:
                require_approval()
        else:
            require_approval()

Now wire that into a Next.js 14 admin UI, server actions, cache invalidation, audit logs, and a permission matrix. Enjoy the bug report: TypeError: Cannot read properties of undefined (reading 'enabled') because one tenant has a partially migrated settings object from August. Then QA asks the obvious question, which combinations are supported. Nobody knows.

This is where velocity dies, not in big rewrites, in ten thousand local exceptions. Your Cypress suite grows from 30 minutes to 95. Product can't predict delivery because every feature estimate starts with "depends which settings it has to respect". Support opens tickets that look trivial and take two days because they require reconstructing a tenant-specific ruleset from toggles spread across four screens. Teams call this flexibility. It's entropy with a nice label.

where config earns its keep

Some configuration is legitimate. Very little of it belongs in the main admin workflow.

Good configuration usually falls into a few buckets. Branding, because logos, colors, email sender names, and custom domains don't explode business logic. External integrations, because connecting QuickBooks, Stripe, S3, or an SMTP relay is inherently environment-specific. Access control, within reason, because assigning people to predefined roles is normal and expected. Numeric thresholds also tend to be safe if they tune behavior without changing the shape of the workflow itself, things like retry limits, invoice reminder cadence, or session timeout.

The line is simple. If a setting changes who can do a thing, when a background job fires, or what label appears in the header, that's probably manageable. If it changes the sequence of core actions, inserts tenant-specific states into a lifecycle, or creates alternative interpretations of the same record, you're building parallel products under one logo.

A decent rule for PMs and founders is this, configuration that tunes a system is cheap, configuration that defines a process is expensive. The first one can often live as typed settings with validation. The second one needs product ownership, migration strategy, docs, training, and permanent engineering budget. Treating both as "just another admin option" is how teams end up with monster Settings screens nobody understands.

We usually push clients toward a narrow admin surface, especially in ERP or document-processing systems, because admins already have enough cognitive load. They don't want to design software behavior. They want to manage users, review exceptions, fix bad data, and move on. That means the product should encode the process. The admin panel should expose the edges, not the whole machinery.

feature flags, not customer knobs

A lot of tenant settings should be feature flags owned by the product team, not knobs exposed to every admin. That's a cleaner contract.

If only three customers need a variant workflow, don't make 300 customers understand it. Put it behind a server-side flag, scope it by tenant ID, track it in code, and document the exit plan. We use boring tools for this on purpose, sometimes Django with django-waffle==4.1.0, sometimes a plain account_features table with explicit columns because SQL beats mystery meat JSON when the behavior matters. A simple model like this is enough:

python
class AccountFeature(models.Model):
    account = models.OneToOneField(Account, on_delete=models.CASCADE)
    invoice_dual_approval = models.BooleanField(default=False)
    custom_export_v2 = models.BooleanField(default=False)
    ai_salesperson_beta = models.BooleanField(default=False)

Then in code, keep the condition close to the boundary:

python
if account.features.invoice_dual_approval:
    return dual_approval_flow(invoice, user)
return standard_flow(invoice, user)

That looks almost too simple, which is exactly the point. Product can turn features on intentionally, support can see the flag state immediately, engineers can grep for the condition, and removal is possible because every exception has a name and an owner. Compare that with a generic settings.workflow.mode = "advanced" plus five sub-options, which means nothing to a new developer and tends to accumulate dead combinations.

Customer-visible settings should be stable, boring, and safe. Internal feature flags can be sharp. Customers don't need access to sharp objects. If a workflow variant proves itself across enough tenants, graduate it into the default product or formalize it as a plugin. If it stays niche, keep it isolated and charge accordingly.

plugins for real variation

Some businesses really do need genuine process differences. That's where plugins beat deep configuration every single time.

A plugin boundary forces honesty. You define extension points, event contracts, validation rules, and failure modes. You stop pretending every workflow belongs in one admin form. For a Django system that might mean signals are banned for core domain logic, because hidden control flow is poison, and instead you expose explicit hooks like before_invoice_finalize, after_payment_reconciled, or build_export_rows. A plugin can register callables, or if you need isolation, run a separate worker that consumes domain events from PostgreSQL or Redis and writes back through a narrow API.

Even a simple Python registry works well:

python
PLUGIN_HOOKS = {
    "build_export_rows": [],
}

def register(hook_name, fn):
    PLUGIN_HOOKS[hook_name].append(fn)

Now the custom weirdness lives in one place, versioned, reviewed, and testable on its own terms. Your default admin workflow stays intact. Your core product docs stay readable. Your support team can say "tenant ACME has export plugin acme_export_v3 enabled" instead of spelunking through settings that half-control data transformation and half-control UI visibility.

This matters a lot for SMB products because one enterprise-ish customer can distort the roadmap for everyone else. A plugin model lets you say yes without rotting the center. It also makes pricing cleaner. Custom process, custom budget. Core workflow, fixed product behavior. People understand that. What they don't understand is why clicking a random checkbox under Advanced Settings broke a webhook and changed approval behavior in the same release.

how to unwind the mess

If your product already has a swamp of admin settings, ripping them out in one shot is a good way to start a customer revolt. You need a slow migration, with receipts.

First, inventory every setting and classify it into four groups, keep as user-facing config, move to internal feature flag, replace with a plugin hook, remove entirely. Be ruthless. Pull actual usage data from the database, not anecdotes from sales. I've seen settings screens where half the toggles were enabled by fewer than 2% of tenants, and three were never changed from the default in two years. Those should disappear.

Second, freeze net-new settings unless they pass a high bar. Write the bar down. A one-page RFC is enough, purpose, owner, expected lifespan, affected code paths, migration plan. If nobody wants to write that, the setting shouldn't exist.

Third, collapse the UI before you collapse the backend. Mark low-value settings as deprecated, hide them behind an "older options" disclosure, add inline copy explaining the new default behavior, then migrate tenants in batches. For high-risk changes, show a tenant-specific notice with dates and actual outcomes, not vague product marketing. "On 2026-04-15, invoice approval will follow the standard two-step flow. Your current threshold of 10,000 CZK will remain." That kind of copy prevents panic.

Fourth, log everything. Audit entries, support-visible feature summaries, migration status, effective workflow description. A support panel with a generated summary like Approval flow: standard, dual approval: off, export plugin: none saves absurd amounts of time.

Last part, the one teams avoid, call customers and walk them through the change. Founders and PMs love abstract statements about flexibility until they're on the phone with an ops manager who admits nobody touched the advanced settings page for a year because they were scared of it. That call is usually where the product gets simpler.

Johnny Unar

Written by

Johnny Unar

Want to work with us?

Config panels feel like product progress until they turn your SMB app into a support machine. Pick one strong admin path and defend it.