Skip to main content

SBPO Consulting · Build

Custom web applications, scoped tightly and built to last

Most failed application projects were not failed engineering. They were failed scoping: a first release that grew until it could no longer be finished, and a specification vague enough that everyone agreed to it. We build software the same way we scope it — one honest priority at a time, with security and permissions designed in rather than added before launch.

Where we come in

The web applications problems this solves

  • You have a specification that everyone signed off and nobody can build from, because it describes features rather than behaviour.
  • The internal process currently runs on a spreadsheet that three people understand and one person maintains.
  • Your customers keep emailing your team for information the system already holds, and nobody has given them a way to see it.
  • A previous team delivered a working prototype and no tests, no documentation and no deployment process anyone can repeat.
  • Every new customer needs a slightly different permission arrangement, and the current answer is a developer editing a database row.
  • The application is fine with the data you have now and visibly slower every month, and nobody has measured why.
  • A prospect has sent a security questionnaire and you cannot answer most of it truthfully.

A web application is not a bigger website

The difference is not size, it is state. A website mostly shows the same thing to everyone and its hardest question is whether the page loads quickly. An application asks a different question on every request: who is this, what are they allowed to see, what has changed since they were last here, and what happens if two of them edit the same record at the same moment.

That shift changes what “finished” means. A marketing page is finished when it is correct and fast. An application is finished when its behaviour is correct under conditions nobody demonstrated: an empty account on day one, a customer with forty thousand records, a permission combination that only exists for one client, a third-party API that returns a 500 halfway through a payment.

Which is why custom web application development is mostly a discipline of deciding things explicitly. The code is the easy part. The expensive part is being clear about what should happen, before someone has built a plausible guess.

What we build

SaaS products. Multi-tenant software sold by subscription, where the hard problems are tenancy isolation, billing and plan logic, onboarding that works without a human, and an administrative surface your own team can operate without a database client.

Customer portals. Somewhere for your clients to see their own data — orders, documents, case status, usage — instead of emailing your team to ask. Portals repay themselves in support volume, and they usually reveal that the underlying data is messier than anyone claimed.

Internal tools and operations software. The replacement for the spreadsheet that three people understand and one person maintains. These projects have the best return and the least glamour, because the requirement is already proven by the fact that someone is doing the work manually today.

Dashboards and reporting interfaces. Where the engineering problem is query performance and the design problem is restraint. A dashboard showing forty metrics answers no question at all; where the value is in the analysis rather than the interface, data and analytics is often the better-fitting engagement.

Discovery and the specification that prevents the argument

Almost every distressed project we are asked to review had a specification. It was usually a list of features with no acceptance criteria, and every person who signed it was imagining something slightly different.

A specification that works describes behaviour. It names the entities and the states each one can be in. It says which role can move a thing from one state to another, and what happens if they try when they should not. It states the rules for the awkward cases — what happens when an invoice is paid twice, when a user is removed while holding a lock, when an integration times out after a partial write.

We reach that by mapping the process as it runs today, including the workarounds people are slightly embarrassed to demonstrate. Those workarounds are requirements. A team that keeps a private spreadsheet alongside the official system is telling you exactly which requirement the official system missed.

The output is a document detailed enough that a competent team who had never met you could build from it. That is a deliberately high bar, and it is the cheapest insurance available on a project of this kind.

Architecture and technology selection

We choose boring technology on purpose. Most business applications are well served by a relational database, a typed application layer and a conventional deployment model, and the reason is not conservatism — it is that you can hire for it, debug it at three in the morning, and find an answer to a problem someone else has already had.

The decisions that genuinely matter tend to be these. Where does state live, and is a relational store the right shape for it? PostgreSQL handles far more than most teams assume, and reaching for a distributed system before you have a scaling problem buys operational complexity with no return. How is work that outlives a request handled — background jobs, queues, scheduled tasks — because “we will do it in the request handler” is a decision that fails quietly under load. Where are the integration boundaries, so a slow third party degrades one feature instead of the whole application? And which parts, honestly, are ever going to need to scale?

Each of these is written down with the trade-off we accepted and the conditions that would justify revisiting it. Architecture decision records take a few minutes each and save the next team from re-deriving your reasoning from the code.

Designing screens that carry real data

Application interfaces do not fail on the layout. They fail on the states nobody drew: the empty account with no data yet, the record with a value forty characters longer than the mock, the user whose role hides three of the five buttons, the request that failed after the optimistic update already ran.

So we design those states explicitly. Every table gets an empty state that tells someone what to do next. Every destructive action gets a confirmation proportional to its consequence, and an undo where an undo is possible. Every asynchronous action makes it visible that something is happening. The Nielsen Norman Group’s response time limits are still the useful reference here: around 0.1 seconds feels instantaneous, one second keeps a user’s train of thought intact, and ten seconds is roughly the limit of attention before people go elsewhere. Those numbers should drive whether an action is optimistic, shows a spinner, or moves to a background job with a notification.

Complex screens also need permission variation designed rather than discovered. An interface that renders differently for six roles is six designs, and pretending otherwise means the seventh combination ships broken.

Scope discipline is the part that protects your budget

The single most useful thing an agency can do on an application project is argue for less in the first release. Not to reduce the work — to make the work finishable.

A good first release contains one complete journey a real user can finish and get value from, plus the things that cannot be retrofitted safely. The permission model is one of those. The data model is another. So is the deployment pipeline, and so is authentication. Teams under pressure invert this constantly: they cut the pipeline and the permission model to keep three more features, and then find that adding real authorisation to a system that never had any means touching every endpoint.

What can wait: reporting, bulk operations, advanced administrative tooling, configurability designed for a requirement nobody has yet stated, and any integration requested by exactly one prospect. Those are not unimportant. They are decisions better made with evidence from real usage than with speculation from a workshop.

Authentication, roles and permissions

Authentication answers who you are; authorisation answers what you may do. They are frequently conflated, and the conflation is where breaches live.

For authentication we normally use OAuth 2.0 with OpenID Connect — the identity layer that sits on top of OAuth 2.0 and returns a signed ID token describing the authenticated user — through a managed provider such as Auth0 or Clerk, unless there is a compelling reason to own it. Session management, password reset, account recovery, brute-force protection and multi-factor enrolment are all easy to get subtly wrong, and a managed provider gets you audited implementations plus enterprise single sign-on, which procurement will eventually ask for.

Authorisation is ours to design and cannot be outsourced. It usually starts as role-based access control, gains resource-level ownership checks, and sometimes needs attribute-based rules where roles cannot express the requirement. Two principles are non-negotiable. Every check runs server-side on every request — hiding a menu item is a user experience decision, never a security control. And the default is deny: access is granted explicitly or not at all.

In multi-tenant systems the tenant boundary belongs in the data access layer, so it is enforced structurally rather than remembered in each query. Then we write tests that deliberately attempt cross-tenant reads and assert failure, because the most damaging defect in business software is not a crash — it is one customer seeing another customer’s data.

Data modelling, APIs and integrations

The data model is the decision with the longest half-life. Interfaces get redesigned, frameworks get replaced, and the schema outlives both. We model the domain as it actually behaves rather than as the first screen implies, keep referential integrity in the database rather than hoping the application maintains it, and treat migrations as reviewable, reversible code from the first day.

APIs get a documented contract before implementation: authentication, resource shapes, error format, pagination, filtering, versioning policy and rate limits. Whether that surface is REST or GraphQL matters much less than whether it is consistent. GraphQL earns its complexity when many different clients need different shapes of the same data; REST is simpler to cache, simpler to debug and usually sufficient.

Integrations deserve pessimism. Every third-party call is a dependency that will one day be slow, rate-limited or wrong. We build them with explicit timeouts, retries with backoff, idempotency keys wherever a duplicate write would cause harm, and a dead-letter path so failures are visible instead of silent. A payment integration through Stripe should never leave you unable to say whether a charge succeeded.

Performance, scalability and honesty about scale

Most applications do not have a scaling problem. They have a query problem, and it usually has the same shape: a list view that issues one query per row, an unindexed column, a report computed on demand that should be computed once, or a payload that returns every field because filtering it felt like premature optimisation.

So the sequence is measure, then fix, then consider architecture. On the server that means query plans, indexes, connection pooling, caching in Redis where the data tolerates staleness, and moving anything slow out of the request path into a job. On the client it means keeping the main thread free, since Interaction to Next Paint — the Core Web Vital for responsiveness, with a target of 200 milliseconds or less — is dominated by long tasks blocking the browser from responding. Data-dense applications are the most common place to fail it, because they ship the most JavaScript.

We do plan for growth, in the sense of not building things that cannot grow: statelessness where it is free, background processing for expensive work, and a load test at a realistic multiple of expected volume before launch. We do not build for hypothetical scale, because the cost of that is paid immediately and the benefit may never arrive.

Where offline capability or installability genuinely helps — field teams, warehouse operations, patchy connectivity — a progressive web app using service workers and the Cache API is often a better answer than a native build. Where the requirement is deep device integration or app store distribution, mobile app development is the honest recommendation instead.

Security, testing and release management

We review against the OWASP Application Security Verification Standard rather than an informal checklist, because a published standard gives both sides a shared reference and a record of what was actually examined. That covers authentication and session handling, authorisation on every endpoint, input validation and output encoding, file upload handling, secrets management, transport security, dependency scanning and rate limiting on anything expensive or abusable. For anything facing enterprise procurement or handling sensitive personal data, we recommend an independent penetration test as well — our review is a floor, not a substitute for adversarial testing by someone with no stake in the outcome.

Testing follows the same logic: cover what would hurt. Unit tests for business rules where the logic is genuinely intricate, integration tests across the API surface, and end-to-end tests in Playwright for the journeys whose silent failure would cost you money or trust. Coverage percentage is a poor target; a suite that runs on every pull request and fails honestly is the actual goal.

Releases go out through an automated pipeline with migrations applied in a controlled order, feature flags where a change is risky, and a rehearsed rollback. Then observability earns its keep: structured logs, error tracking in Sentry, uptime monitoring and alerts that reach a person. The measure of a well-run application is that you hear about problems from a monitor rather than from a customer.

After launch

The first weeks of real usage teach you more than the whole specification did. Users take paths nobody predicted, break assumptions politely, and ignore the feature that took longest to build. That is not failure, it is the return on shipping something small.

We prioritise the next iteration from observed behaviour and support volume rather than from the original roadmap, and we keep the handover obligations continuous rather than saving them for an ending: documentation stays current, dependencies stay patched, and your engineers stay able to take over whenever you want them to.

What drives the cost

We do not publish figures, because the range is genuinely wide and any number quoted before the specification exists is a guess dressed as a commitment. What moves it: the number of user roles and the permission complexity between them; how many entities exist and how many states each moves through; single-tenant versus multi-tenant; the number and quality of integrations, where an undocumented legacy API can cost more than a whole feature; whether data must be migrated from an existing system; and compliance obligations such as SOC 2 readiness or sector-specific retention rules, which are engineering work rather than paperwork.

The variable clients underestimate most often is their own decision-making speed. An application project generates a steady stream of questions that only you can answer, and the cost of a slow answer is idle build capacity. We make that visible in the plan instead of discovering it later.

Adjacent work

An application usually needs a public site in front of it — a marketing site, documentation, a pricing page — which is web development rather than this. Where the interaction problems are the hard part, interface and experience design runs alongside the build rather than before it. If the same audience needs a native experience on a phone, we would talk about building for mobile as a deliberate second decision rather than an assumption. And where the application’s value depends on prediction, classification or extraction from unstructured data, that is AI and machine learning work with its own evaluation requirements.

If you have a specification and want it challenged before anyone builds from it, that is a conversation worth having early.

Scope

What our custom web application development include

Every engagement is scoped in writing before it starts. These are the artefacts that leave our hands and become yours.

  1. A technical specification you could hand to another team

    User roles, core entities, the states each one moves through, the rules governing those transitions, and the acceptance criteria for each. Written so it can be built from, argued with and estimated against, rather than admired in a kick-off meeting.

  2. Data model and API contract

    An entity relationship model, migration strategy and documented API surface — REST or GraphQL — with authentication, error shapes, pagination and rate limits defined before implementation rather than emerging from it.

  3. A working, deployed application

    Running in your cloud account on infrastructure defined as code, with separate environments for development, staging and production, and a deployment that any competent engineer can trigger and reverse.

  4. Authentication and an explicit permission model

    Roles, scopes and resource ownership rules written down and enforced server-side, with the tenancy boundary tested. The permission model is a document as well as a code path, because it is what a security reviewer will ask to see.

  5. An automated test suite that runs in CI

    Unit tests for business rules, integration tests for the API surface, and end-to-end coverage in Playwright for the journeys that would hurt most if they silently broke. Test coverage is a maintenance instrument, not a vanity number.

  6. Monitoring, error tracking and alerting

    Structured logs, error reporting through Sentry or equivalent, uptime checks and alerts routed to a human who can act. You should learn about an outage from a monitor, not from a customer.

  7. Security review record

    A written pass against the relevant OWASP ASVS requirements, dependency vulnerability scanning in the pipeline, and a documented list of the risks we accepted and why — which is the part most reviews leave out.

  8. Runbooks and developer documentation

    Local setup, architecture decisions and their reasoning, deployment and rollback steps, credential rotation, backup restoration, and the operational tasks someone will need at an unsociable hour.

  9. Handover and access transfer

    Repositories, cloud accounts, domains, third-party API credentials and CI configuration transferred into your ownership, with a walkthrough for whoever takes over — including your own engineers.

How it runs

How SBPO Consulting delivers web applications

  1. Discovery against the process, not the wish list

    We map how the work happens today, including the workarounds people are embarrassed about, because those are the requirements nobody writes down. Feature lists describe what someone imagines; process maps describe what actually has to be true.

  2. Specify behaviour and agree acceptance criteria

    Entities, states, transitions, permissions and edge cases in writing, with acceptance criteria per requirement. This is the document that prevents the expensive argument, and it is cheaper to argue over it now than over a built feature later.

  3. Choose the architecture and justify it in writing

    Data store, hosting model, rendering strategy, background job handling and integration boundaries — each recorded with the trade-offs accepted, so a future team inherits reasoning rather than archaeology.

  4. Design the interface around the data density

    Complex screens fail on empty states, long values, permission variations and error handling rather than on layout. We design those states explicitly, alongside our UI and UX practice where the interaction problems justify it.

  5. Build in vertical slices

    Each iteration delivers one complete journey — interface, API, data, tests and permissions — deployed and usable. Building horizontally, one layer at a time, produces a project that is ninety per cent complete for a very long time.

  6. Test, review and harden before release

    Automated suites in CI, a manual pass on permissions and tenancy boundaries, dependency scanning, load testing at a realistic multiple of expected volume, and an accessibility check on the interactive components.

  7. Release, observe, then iterate on evidence

    Ship behind feature flags where it reduces risk, watch error rates and real interaction latency, and prioritise the next iteration from what users actually do rather than from what the roadmap assumed in month one.

Tooling

Tools and platforms we use for web applications

We pick tools for the problem, not for the résumé. Where a platform is a poor fit we will say so before you have paid for it.

Application

  • TypeScript
  • React
  • Next.js
  • Node.js
  • Python
  • tRPC / GraphQL / REST

Data

  • PostgreSQL
  • Redis
  • Prisma
  • Elasticsearch
  • S3-compatible object storage

Infrastructure

  • Amazon Web Services
  • Docker
  • Kubernetes
  • Terraform
  • Vercel
  • Cloudflare

Identity and payments

  • OAuth 2.0
  • OpenID Connect
  • Auth0
  • Clerk
  • Stripe

Quality and operations

  • Playwright
  • Jest / Vitest
  • GitHub Actions
  • Sentry
  • OpenTelemetry

Non-negotiables

The standards SBPO Consulting works to

These are checkable. Ask us to demonstrate any of them on your own project before you sign anything.

  1. Authorisation enforced server-side, deny by default

    Every request re-checks permission against the resource, regardless of what the interface allowed. OWASP guidance is explicit that client-side access control checks must never be relied on, and hidden menu items are not a security model.

  2. Reviewed against OWASP ASVS

    We work to the Application Security Verification Standard rather than an informal checklist, and record which requirements were met, which were not applicable, and which risks were consciously accepted.

  3. Infrastructure as code, no manual production changes

    Environments are defined in Terraform or equivalent and reproducible from the repository. Configuration applied by hand in a console is undocumented by definition and unrecoverable after the person who applied it leaves.

  4. Tenancy boundaries tested, not assumed

    In any multi-tenant system we write explicit tests that attempt cross-tenant access and expect them to fail. The most damaging bugs in business applications are not crashes, they are one customer seeing records that belong to another.

  5. You own the accounts and the code

    Cloud accounts, repositories, domains and third-party credentials are registered to you from the first day. Handover is a formality, not a negotiation.

Questions

custom web application development — questions we get asked

How is a web application priced compared with a website?

A website is priced largely on the number of distinct templates; an application is priced on behaviour, and behaviour multiplies. The drivers are the number of user roles, the number of entities and the states each moves through, how many permission rules exist, the volume and quality of third-party integrations, and whether the system is single-tenant or multi-tenant. Compliance obligations move the figure more than most clients expect, because audit trails, data retention rules and access reviews are engineering work rather than paperwork. We estimate against a written specification rather than a feature list, and if you ask for a number before that specification exists, the only honest answer is a range wide enough to be useless.

What should go into a first release and what should wait?

A first release should contain one complete journey that a real user can finish end to end and get value from, plus the operational things you cannot retrofit safely — authentication, the permission model, the data model and the deployment pipeline. Everything else is a candidate to defer. The usual mistake is inverting this: teams cut infrastructure and security to preserve features, then discover that adding a permission model to a system that never had one means rewriting most of it. Reporting, bulk operations, advanced admin tooling, integrations that only one customer has asked for and configurability that anticipates a future requirement can nearly always wait for evidence.

Can you take over an existing codebase from another team?

Yes, and we start with a paid technical review before committing to a scope, because quoting on an unread codebase is unfair to both sides. That review covers whether the build is reproducible from a clean checkout, test coverage and what it actually asserts, dependency and vulnerability status, the security posture of the authentication and authorisation layers, and how much of the existing work is worth preserving. We give you the findings in writing whether or not you then hire us to continue. Sometimes the recommendation is to keep the code and fix the process around it, and sometimes it is that the archaeology costs more than a rebuild.

How do you handle user roles, permissions and multi-tenancy?

Permissions are designed as an explicit model rather than accumulated as conditional statements. That usually means role-based access control with resource-level ownership checks, and attribute-based rules where roles alone cannot express the requirement. Every check happens server-side on every request, because the OWASP Authorization Cheat Sheet is unambiguous that client-side checks must never be relied upon. For multi-tenant systems we make the tenant boundary structural — enforced in the data access layer rather than remembered in each query — and we write tests that deliberately attempt cross-tenant access and assert that it fails.

What does your security review cover before launch?

Authentication flows including session handling, password reset and multi-factor enrolment; authorisation on every endpoint including the ones only the interface is supposed to reach; input validation and output encoding; file upload handling; secrets management; transport security and security headers; dependency vulnerability scanning; and rate limiting on anything expensive or abusable. We work against the OWASP Application Security Verification Standard so the review has a published reference rather than being our own opinion. For applications handling sensitive data or facing enterprise procurement, we recommend an independent penetration test as well — our review is a floor, not a substitute for adversarial testing by someone with no stake in the result.

Will our own developers be able to continue the work later?

That is an explicit design goal, and it changes real decisions. We favour mainstream, well-documented technologies over clever ones, keep the architecture as boring as the problem allows, and write down the reasoning behind decisions so the next engineer inherits context rather than mystery. Deliverables include local setup instructions, architecture decision records, runbooks and a walkthrough with your team. A useful test to apply to any agency: ask what happens if you hire two engineers and want to bring the work in-house next year. If the answer is uncomfortable, that tells you what the arrangement is really built on.

How long does a typical MVP take to reach production?

Duration is set by scope and by decision speed, so quoting a number before either is known would be dishonest. What reliably compresses a schedule: a single decision-maker with authority, a written specification agreed before building starts, integrations whose APIs are documented and whose credentials already exist, and a first release limited to one complete journey. What reliably extends it: committee approvals, a third-party system whose API turns out to be undocumented or unstable, data migration from a legacy database nobody owns, and scope added after the estimate. We plan in iterations with a demonstrable increment at the end of each, so you can see the pace and adjust the scope rather than waiting for a date to arrive.

Adjacent work

Web development

Fast, secure, maintainable websites built in modern frameworks and CMS platforms — engineered so performance and accessibility survive the first year of edits.

UI/UX design

Research-led interface and experience design for products people use repeatedly — flows, states, accessibility and a design system engineering can actually build from.

Mobile apps

Native and cross-platform apps for Android and iOS, scoped honestly, designed to platform conventions, and maintained through the OS releases and store deadlines that arrive every year.

Build

Let's talk about your web applications work.

Send us the problem, the constraint and the deadline. You will get a considered reply from someone who would actually do the work — not a templated proposal.