Web Development

Web development at Software Mile means applications, not brochures – the systems your team logs into every day: portals, dashboards, internal tools, and customer-facing platforms built on modern web stacks and tied into the data behind them.

What We Build for the Web

  • Customer and partner portals with authentication, roles, and self-service workflows
  • Internal operational tools that replace the spreadsheet that became a system of record
  • Dashboards and reporting front ends over your real data
  • Web applications integrated with the APIs and systems you already run

Built to Last Past Launch

We build with mainstream, well-supported frameworks (React and the like) so your application is maintainable by any competent developer – not locked to us or to a technology nobody will support in three years. You get the source, documentation, and a clean codebase from day one.

Describe the web application you need – who uses it, what it connects to, and what it should replace. We will scope it after discovery.

Not Everything Needs to Be a Web Application

Sometimes the smaller answer is the right one. If what you need is content, credibility, and a contact form, that is a website, and it should not be built as an application. If a small team needs a shared list with a few fields, a spreadsheet or an off-the-shelf tool may genuinely be enough, and we would rather say so than sell you a build.

A web application starts to earn its keep when there are roles and permissions, a workflow with states, an audit trail somebody will eventually ask for, integration with systems that already hold the data, or a volume of records that breaks a spreadsheet. If the work happens away from a desk or needs the device itself, such as camera, location, or offline use, look at mobile app development or cross platform app development first.

Build or Buy, and How Do You Tell?

Buy where the process is standard and defined by someone other than you: accounting, payroll, email, general-purpose CRM. There is little advantage in a bespoke version of a solved problem, and a product serving many customers usually keeps improving in ways a single build will never be funded to match.

Build where the process is the thing that makes you different, or where every product you evaluated needs enough customization that you end up maintaining someone else’s code inside a license you still pay for. Heavily modified off-the-shelf software can leave you carrying both sides at once: the work of custom development and the constraints of a vendor. The middle path usually holds up. Buy the platform, build the parts unique to you, and connect them through APIs.

The signals that push the decision one way are usually visible before anyone writes code.

  • How much of the process is genuinely yours, and how much is the same in every company
  • Whether the products you evaluated need configuration or need modification
  • Who is available to run an upgrade cycle once the system is live
  • What the data has to connect to, and whether that connection is supported or improvised
  • How long the process is expected to stay the way it is today

Does Server-Rendered or Single-Page Matter for Your Case?

For an internal tool behind a login, much less than the arguments suggest. Search engines never see it, users are on known networks, and the interface benefits from behaving like an application. For public pages where search visibility and first paint decide whether anyone arrives at all, server rendering matters and should not be an afterthought.

Plenty of systems want both, and the clean answer is usually to render the public surface on the server and keep the application behind the login. The argument worth having instead is about the data model and the integrations, because those decide whether the system is pleasant or painful to change three years from now.

Why Do Internal Tools Get Abandoned?

A tool gets abandoned when it makes someone’s day slower. A replacement for a spreadsheet has to beat the spreadsheet on the first day it is used, not after a training program. That means importing the data people already have, keeping an export so nobody feels trapped, and not adding approval steps that did not exist before.

A second pattern is who gets consulted. When the design conversation happens with the people who want the reports and never with the people who do the work, the daily user loses time so a manager gains a dashboard. The tool then gets bypassed and the spreadsheet quietly comes back. Discovery is better spent with the handful of people who will open it every day.

What Should You Own at Handover?

The test we design toward is whether a different firm could pick this up. That means source in a repository you control, a documented setup that works on a clean machine, a deployment pipeline instead of a person who remembers the steps, notes on the data model and the integrations, and cloud and domain accounts in your company’s name.

Nobody inherits a system for free. Our own support page is direct about that: taking over someone else’s codebase is real work, and the first weeks run slower while it is done. What a documented handover changes is how much of that work there is. Our stack choices are made with the same test in mind, and the reasoning behind them sits on our development tools and frameworks page. If you would rather keep the work in-house afterward, staff augmentation adds capacity to your own team without transferring ownership to a vendor.

What the Build Depends On From Your Side

The order that holds up is discovery, then the data model and an inventory of every system it has to talk to, then a thin slice that runs all the way from one screen to the database and out to whatever system sits behind it. That slice is worth building before anything looks finished, because it is where the surprises live: the field that is free text in one system and a code list in another, the export that turns out to be a formatted report rather than a feed, the login integration that needs an administrator nobody has met yet.

Several of the items below have lead times that belong to other people, which is why naming an owner for each one at the start is worth more than any schedule. Where one of them cannot be supplied when it is needed, work continues against stub data and a fake response, but that defers the risk rather than removing it, and the integration still has to be proven against the real thing before anyone relies on it.

  • Credentials for every system the application reads from or writes to, in a test environment as well as production
  • Real data, including the records that are wrong: duplicates, blank required fields, the customer who was entered three times under slightly different names
  • One person who knows how the process actually runs today, including the exceptions that are currently handled by hand
  • A decision on who is allowed to see which records, not only which screens
  • An administrator for your identity provider, who has to create the application registration and consent to it
  • The name of whoever accepts the work, and what they need to see before they will

How Sign-In Usually Gets Wired

If your staff already sign in to Microsoft Entra ID, Google Workspace, or Okta, the application should use that rather than keep a password table of its own. The mechanism is OIDC or SAML: an application registration in the directory issues a client ID and a secret, every redirect URI has to be registered exactly as the browser will send it, and the application never sees or stores a password. Offboarding then collapses to one step, because disabling the directory account ends access to the application with it.

External users are a separate problem. Customers, partners, and subcontractors usually cannot be put in your corporate directory, so they get their own sign-in path with email verification and self-service password reset. Two sign-in paths in one application is normal and manageable, provided both end at the same user record, so that permissions, audit entries, and reports do not have to be written twice.

Roles decide which screens exist. They do not by themselves decide which rows a person can see, and that is the distinction that gets skipped. Something has to scope a record: the branch it belongs to, the account manager on it, the customer it is for. Deciding that late means either everyone sees everything until someone notices, or filter conditions sprayed by hand through every query, where the one that gets missed is the bug you find out about from a customer.

What an Integration Needs From the Other System

An integration needs three things from the system on the other end: a way in, a way to identify the same record on both sides, and a way to know what changed since last time. A documented REST API with OAuth credentials gives you all three. Where there is no API, the workable fallbacks are a nightly file drop to SFTP or read access to a database, and both are fine, but neither tells you what changed unless there is a reliable modified timestamp or you are willing to compare the whole set each run.

Record matching is the part that is consistently underestimated. Two systems rarely share a key. Email addresses get reused and shared, company names differ by punctuation, and people change both. Decide early whether you store the other system's identifier on your own record, which is cheap and permanent, or match on attributes every time, which is neither.

For every field that exists in more than one system, one of them owns it and the rest display it. Writing that down before code is written is what prevents the failure that is hardest to explain afterward: someone corrects an address in the new application, a sync from the old system overwrites it that night, and nobody can say which value was right or when it changed. Two-way sync is possible, but it is only safe field by field, with an owner named for each one.

Getting Off the System You Are Replacing

Migration is a build of its own, not a step at the end. It is an extract, a transform, a load, and a reconciliation that counts what arrived against what left and reports the difference. It gets run repeatedly into a scratch environment before it is run for real, because the first pass always turns up records the old system tolerated and the new schema does not: the required field that is empty in a few percent of rows, the date stored as text, the status value that exists only in records from before a process changed.

How much history moves is a decision rather than a technical fact. Open and recent items always move. Closed history often does not need to, and can sit in a read-only archive or an export that stays available. Moving everything sounds safer and mostly buys cleanup work on data nobody will open again, so it is worth asking what someone would actually do with a record from eight years ago before paying to clean it.

The last decision is what happens to the old system. Leaving it writable after cutover produces two systems of record and a slow drift back to the familiar one. Read-only for a defined period is the usual compromise, because it keeps the lookup available without letting anyone add to it. Set the date it goes read-only and the date it goes away, and check who is paying its license in the meantime, since that is often a different budget from the one funding the replacement.

What the Application Needs After It Is Live

A web application is a running system whose dependencies age whether or not anyone touches the code. Runtimes are on published support clocks: an even-numbered Node.js release line gets about 30 months of long-term support once it enters LTS, and a PostgreSQL major version is supported for five years from its release. Framework majors, browser behavior, and the APIs of everything you integrate with move as well. None of it is urgent in any given month, and all of it is expensive at once if nobody has looked for three years, because the upgrades then have to be done in sequence rather than as they come.

The recurring work below is not large, but it has to belong to a named person or team, whether that is your own staff, us, or another firm. Some of it runs on your schedule and some of it does not: certificates expire on a fixed date, and API credentials expire on the vendor's timetable, sometimes with notice sent to whichever mailbox was used to register the account.

  • Dependency and runtime updates, applied on a regular cycle and tested before they ship
  • TLS certificate renewal, automated where the host supports it and monitored where it is not
  • Rotation of API keys and client secrets, including the ones a vendor will expire whether or not you are ready
  • Backups that have been restored somewhere at least once, rather than backups that are merely scheduled
  • Error and uptime monitoring that reaches a person, not a mailbox nobody opens
  • A route for users to report what broke, and someone whose job it is to read it

Frequently Asked Questions

Which of these dependencies usually takes the longest to arrange?

Anything that has to come from a third party, because it moves on another organization's timetable rather than yours. A vendor API key can require a support ticket, approval from whoever owns the account commercially, and a sandbox that is provisioned separately from production with different credentials. Some vendors also require the calling IP address to be allowlisted, which pins down hosting before the environment exists. Ask for those first, ahead of anything that can be sorted out internally in an afternoon.

Who hosts the application, and where does the domain live?

Either in your own cloud account or in one set up for the project, and in both cases hosting and DNS should end up registered to your company rather than to whoever set them up. Practically the application needs a hosting environment, a managed database, somewhere to put uploaded files, and DNS records you can edit. DNS matters more than it appears: the TLS certificate, the cutover itself, and email sending all depend on it, and if the application sends mail from your domain, the SPF, DKIM, and DMARC records have to be added there or the messages land in spam.

If we use single sign-on, does that handle permissions too?

No. The identity provider answers who someone is; the application still has to answer what they are allowed to do. Group or role claims can be passed in the token and mapped to application roles, which is worth doing, but somebody has to own group membership in the directory, and that person is usually in IT rather than in the department that uses the tool. Multi-factor and password policy also live at the identity provider, so the application inherits whatever is configured there and cannot tighten it on its own.

What if the system we need to connect to has an API but limits how often we can call it?

Then the application keeps its own copy and synchronizes on a schedule instead of calling the vendor on every page load. A screen that reads live from a third party inherits both that vendor's rate limit and its downtime, so anything on a frequently used path gets a local copy with the vendor's record identifier stored alongside it. Where the vendor supports webhooks, they cut the polling frequency down but do not replace it. Webhooks get missed during outages, so a periodic reconcile stays in place to catch what did not arrive.

Do we need a test environment as well as the live one?

Yes for anything with integrations, and the question worth asking early is whether each connected system offers a sandbox of its own. Many do not, and then the test environment either points at production with read-only credentials or runs against recorded responses, which is a deliberate choice with consequences for who can see real data. Copying production data into a test environment carries the same personal information with it, so it either gets masked or the environment gets the same access restrictions as production.

What happens when a requirement appears halfway through the build?

It gets sized, and then it is either taken in by dropping something of similar size or written down for a later phase. What decides which is usually how deep it goes: a new screen or an extra report can wait, while a change to the data model or to the relationship between two kinds of record gets more expensive the later it lands, because everything built on top of it has to be revisited. Requirements that surface once people see real screens are normal and worth listening to, since they tend to be the ones that come from the actual work.

People will use this on their phones. Does that mean we need a mobile app?

Offline use and real device access are the reasons the section above points at a mobile build first, so if either is a hard requirement, start there. Short of that, a responsively built web application runs in a mobile browser and can reach the camera and location through standard browser APIs, with the user granting permission per site and only over HTTPS. The useful test is whether the phone is central to the work or merely convenient. A technician completing a form in a basement with no signal needs something installed on the device; a manager approving a request between meetings does not.