Custom web apps.
Overview
Some problems outgrow a website. A customer portal, a booking system, a dashboard that pulls numbers from three different tools, an internal app that replaces a spreadsheet everyone is afraid to touch.
You can usually name the spreadsheet. It has one owner, it lives on someone’s desktop, it has a tab nobody understands, and the business would stop for a day if it were lost. The job of a first release is rarely to reinvent how you work. It is to move that one process somewhere it can be shared, checked and backed up.
We build these as small, focused web applications: a clear data model, a fast interface, and an API other systems can talk to. The stack is mainstream on purpose, TypeScript, Node or PHP, and Postgres, so you’re never locked into us to keep it running.
One screen, three jobs
Here is the part most people underestimate. An app is not a website with a login on it. The same screen has to be a different tool depending on who opened it: what they can see, what they can change, and what they are never told exists.
Have a go. Change the role, search, sort a column, filter by status.
Nothing matches that search.
Notice what the customer view does. It is not hiding rows with CSS, it is asking for a smaller set. That distinction is the whole of application security in one sentence: if the browser can ask for it, the browser will eventually get it, so the decision has to be made where the data lives.
The API is the product
Every app we build is an API with an interface on top, rather than an interface with some database queries behind it. It costs no more to do it that way and it changes what the thing can become: your accounts package can pull invoices, your warehouse can push stock, a second app can be built later without anyone reverse-engineering the first.
Send a couple of requests and swap the token while you are there.
Every job the signed-in token is allowed to see, filtered and paged.
GET /v1/jobs?status=in_progress// the response appears here
Canned responses, running entirely in this page. The shapes and the status codes are the ones we would actually ship.
Scoping the first release
The longest projects we have seen go wrong were the ones that tried to specify everything before building anything. We scope the smallest version that is genuinely useful, put it in front of real users, and let their behaviour set the next priorities.
- 01
Understand the process
24 hoursWe sit with the people who do the work today and watch them do it, including the workarounds. The spreadsheet, the WhatsApp group and the paper diary are all part of the system, whether or not anyone has written them down.
A written description of how it works now - 02
Model the data
Week 1Before any screens, we agree what a job, a customer or an order actually is, and what it cannot be. Most of the pain in a long-lived app traces back to a data model that was guessed at in week one and never revisited.
A schema and the rules around it - 03
Build the first slice
Weeks 2–3One workflow, end to end, properly finished rather than five half-built. It goes on a staging environment from the first week so you are reacting to something real instead of a picture of it.
A working app on staging - 04
Harden and test
Week 3Automated tests go around anything touching money, permissions or data you cannot recreate. Roles get tested as roles, which means trying to reach things you should not be able to reach.
A tested build and a deploy that rolls back - 05
Launch and watch
Week 4We put it live for a small group first, watch the logs and the error tracker, and fix what only real use reveals. Then it opens to everyone, with monitoring and backups already running.
Live, monitored, and yours
Typical shape of a first release, live within four weeks. Every project is scoped on its own.
What we build
The kinds of thing
- Customer portals, where your clients see their own records
- Booking and scheduling, with availability that cannot double-book
- Internal tools that replace a spreadsheet or a shared inbox
- Dashboards that pull numbers from systems that do not talk
- Integrations, so two pieces of software stop needing a human between them
What comes as standard
- Roles and permissions enforced by the API, not the interface
- An audit trail on anything that changes money or status
- Staging and production, with the same deploy running to both
- Automated tests around the parts you cannot afford to get wrong
- Error tracking and uptime monitoring from day one
- Exports, because your data should never be hostage to our app
Boring on purpose
We pick technology you can hire for. TypeScript and Node, or PHP where it fits the team you already have. Postgres for anything with relationships in it. Plain server-rendered pages unless the interface genuinely needs to be an application in the browser.
None of that is fashionable, and that is the point. An app you commission this year has to be maintainable in four years, possibly by somebody else. Every clever framework choice is a bet that the next developer will know it too. We would rather make the bet that they know SQL.
Where something does need to be quick and interactive, we build that part properly rather than pulling in a framework for the whole site to serve one screen. The two prototypes above are that argument in miniature: both are a few kilobytes of plain JavaScript.
What you get
- A web app built around your workflow, not a template
- A documented API, so the next system can talk to this one
- Roles and permissions designed before the screens, and tested as roles
- Staging and production environments with repeatable, reversible deploys
- Accessible, responsive interfaces that work on a phone in a yard, not just a desk
- Your data exportable in a standard format, on request, at any time
- Hosting, monitoring and support after launch, if you want it
Questions we get asked
How do we know what it will cost?
We scope the first release before quoting it, and the scoping is a paid, separate piece of work you can take elsewhere. That is deliberate. A fixed price on a vague brief is either padded to cover the unknowns or it becomes a fight later, and neither of those is a good start.
Can it talk to the software we already use?
Usually. If it has an API, yes. If it only exports a file on a schedule, we can work with that too, though it will be less immediate. The honest answer depends on the system, and we will check before promising rather than after.
What happens if we want to take it elsewhere?
You can. The code is yours, the stack is mainstream, the database is standard Postgres and the API is documented. There is no runtime licence and nothing that phones home to us. We would rather keep clients because leaving is easy and they choose not to.
Do we need to know exactly what we want first?
No, and we would be a little suspicious if you did. You need to know the problem. Working out the solution in detail before anyone has used anything is how projects end up with features that were specified, built, and never opened.
Who can see what, and how do you prove it?
Roles are designed before the screens and enforced at the API. They are also tested as roles, which means automated tests that sign in as a customer and try to reach an owner-only record, and fail. Try the customer token in the API panel above.
What about our data if something goes wrong?
Backups run off-site on a schedule and are restored periodically, because a backup nobody has restored is a hope rather than a plan. Anything that changes money or status leaves an audit record, so you can answer who changed what and when.