Skip to content

Software Engineering

Database Development

We design and build the data layer — the schema everything else is built on — and rescue databases that have turned slow, tangled or unreliable.

Overview

This is the delivery service for the layer underneath everything else: the database. You bring a data model that needs designing properly the first time, a schema that has grown into something nobody wants to touch, or an application that has quietly become slow because the database underneath it is doing far more work than it should — and we design it, build it, tune it and operate it. The distinction from a generic “we do databases” pitch is that we own the parts that only hurt later: the schema shape a hundred features will depend on, the indexes that decide whether a query takes two milliseconds or two seconds, and the migration discipline that lets you change the structure without a frightening maintenance window.

The uncomfortable truth about this work is that the schema is the foundation everything else is built on, and it is the most expensive thing in the whole system to change once real data and real code depend on it. The day your application, your reports and your integrations are all reading and writing a table, you can no longer split it, rename a column or fix a modelling mistake without a migration, a deployment plan and a way to roll back. So the money is not in typing out `CREATE TABLE` — that is the easy, cheap part — it is in getting the model right before the data piles up behind it. We spend real effort at the front of an engagement on the entities, the relationships, the constraints and the access patterns, precisely because those are the decisions that are cheap now and ruinous to unwind in eighteen months.

We work across the storage engines and choose per access pattern rather than picking a favourite and forcing everything through it — though we will say plainly that a relational database, and PostgreSQL in particular, is the right default for the overwhelming majority of systems, and the bar for giving up its guarantees should be high. Where a document store genuinely fits the shape of the data and the way it is read, we reach for one; where it is being chosen out of a vague fear of future scale, we push back. That honest call, made on your workload rather than on what looked good in a conference talk, is a large part of what you are paying for.

Who it’s for — Teams whose data model is about to become load-bearing and needs designing properly the first time, or whose existing database has become slow, tangled or unreliable and needs a senior to make it fast and trustworthy again.

What you get

  • A designed, documented data model — entities, relationships, keys and constraints — with the normalisation and the deliberate exceptions to it written down and explained
  • A schema built and migrated into production, with a repeatable, reversible migration process rather than hand-edited structure nobody can reproduce
  • The relational-versus-NoSQL decision made explicitly per access pattern, argued from your reads and writes, not from fashion or fear of scale
  • Indexing and query optimisation — the right indexes for the queries you actually run, the N+1 patterns found and killed, slow queries traced to their cause and fixed
  • Data integrity enforced in the database — foreign keys, unique and check constraints, sensible defaults — so bad data cannot get in through a code path someone forgot
  • Replication, automated backups and a restore procedure that has actually been tested, plus the read-replica and partitioning strategy where the load warrants it
  • Monitoring on the things that predict trouble — slow queries, lock contention, replication lag, connection saturation — and a handover so your own engineers can operate and extend it

What Database Development does for you

  • A foundation that is cheap to build on later

    Every feature, report and integration you ship sits on the data model. Get it right and adding to the system is straightforward; get it wrong and every change fights the schema. We front-load the modelling work — entities, relationships, constraints, access patterns — precisely because it is the layer that either quietly saves you years of pain or quietly creates it, and it is far cheaper to get right before the data accumulates than after.

  • Applications that are actually fast

    Most “the app is slow” problems are not application problems at all — they are database problems: a query with no supporting index scanning the whole table, or an ORM firing one query per row in a loop. We find them by measuring rather than guessing, add the indexes that match your real queries, and kill the N+1 patterns, so the system is fast because the data layer is doing the right amount of work, not because you threw more hardware at the wrong problem.

  • Integrity that does not depend on remembering

    Data that is wrong is worse than data that is missing, and application-only validation eventually lets something through — a new endpoint, a background job, a manual fix that skips the checks. Enforcing foreign keys, uniqueness and check constraints in the database means the rules hold no matter which code path touched the data, so you are not one forgotten validation away from silent corruption you only discover months later.

Why teams choose us for Database Development

  • You are designing a data model that will be load-bearing for years, and you want the schema, keys and constraints done by engineers who have felt what a bad model costs to unwind — not treated as a detail to sort out once the features are half-built on top of it.
  • Your application has slowed down and you want someone who will find the actual cause in the database — the missing index, the N+1, the query scanning millions of rows — by measuring it, rather than papering over it with a bigger instance or a cache that hides the problem.
  • You are weighing relational against NoSQL and want a straight answer argued from your access patterns, including the honest case for why PostgreSQL is probably the right default and why “we might need to scale” is usually the wrong reason to give up relational guarantees.
  • You care about the data surviving — tested backups, replication, safe reversible migrations, integrity enforced where it cannot be bypassed — built in as how the database is run, not discovered missing the day you need to restore.

What Database Development includes

The concrete pieces of work this covers — scoped to what your problem actually needs.

  • Data modelling and schema design

    We design the model before writing tables — the entities, the relationships between them, the keys, and the constraints that keep the data honest. We normalise to remove the duplication that lets data contradict itself, and denormalise deliberately and visibly where a specific read pattern justifies the trade, rather than by accident. The output is a schema that reflects how the business actually works and that a new engineer can understand, with the modelling decisions written down and defensible.

  • Choosing relational versus NoSQL per access pattern

    The storage engine is chosen on how the data is read and written, not on fashion. For the overwhelming majority of systems a relational database — PostgreSQL as the sensible default — is the right answer, and its transactions, joins and constraints are guarantees you should not give up lightly. A document store earns its place when the data is genuinely document-shaped and read as whole documents. We make that call explicitly, and we push back hard on adopting NoSQL out of a vague fear of scale you may never reach.

  • Indexing and query optimisation

    Most database performance work is indexing done well: the right index for the queries you actually run, composite indexes ordered correctly, partial and covering indexes where they earn their keep, and the discipline not to index everything and pay for it on every write. We read query plans rather than guess, find the queries scanning far more rows than they return, kill the N+1 patterns an ORM quietly produces, and turn the slow endpoints back into fast ones by fixing the cause rather than caching around it.

  • Migrations and safe schema change

    Schema change is where databases get broken, so we make it a controlled, repeatable, reversible process — migrations in version control, applied the same way in every environment, written to run against a live system without a frightening lock or a long maintenance window. A column split, a table rename or a constraint added on a large table is planned as the multi-step, backwards-compatible operation it needs to be, with a rollback path, rather than a single risky statement run against production at midnight.

  • Data integrity and constraints

    We enforce the rules of your data in the database itself, where no code path can skip them: foreign keys so a record cannot reference something that does not exist, unique constraints so duplicates cannot creep in, check constraints for the invariants that must always hold, and sensible defaults and not-null rules. Application validation is a convenience for users; the database is the last line that guarantees correctness regardless of which endpoint, job or manual fix touched the data.

  • Replication, backups and performance tuning

    The operational layer that keeps the data available and recoverable: replication for redundancy and for offloading reads, automated backups with point-in-time recovery, and — the part most people skip — a restore that has actually been tested, because an untested backup is a hope, not a plan. On top of that, ongoing performance tuning: connection pooling, vacuum and statistics health, lock-contention investigation, and the configuration that matches the database to the hardware it runs on rather than leaving it on defaults.

Where it fits

  • Designing the data model for a new system

    A greenfield build where the schema is about to become the foundation for everything. We model the entities and relationships around the real access patterns, choose the storage engine deliberately — usually relational, usually PostgreSQL — and set up keys, constraints, indexes and a migration process from the first table. The value here is entirely front-loaded: an hour of modelling now saves weeks of painful migration later, because the model is the thing you cannot cheaply change once features and data sit on it.

  • Rescuing a database that has become slow

    An application that has gradually slowed to the point users complain, where the cause is in the data layer. We measure rather than guess — read the query plans, find the missing indexes, the N+1 loops and the queries scanning millions of rows to return a handful — and fix them. Often a system that felt like it needed a rewrite or a much bigger instance just needed the right three indexes and an N+1 pattern removed, and comes back to responsive without touching the hardware.

  • Untangling a schema that grew organically

    A database that started clean and accreted years of expedient changes: duplicated data that now disagrees with itself, columns whose meaning has drifted, nullable fields standing in for missing structure, and changes that feel too dangerous to attempt. We map what is really there, design the model it should have been, and sequence a safe migration towards it in reversible steps — so the schema becomes something your team can change with confidence again, without a big-bang rewrite that stops everything.

  • Making the data resilient and scalable

    A system whose data has become too important to lose or too heavily read to serve from a single node. We put in tested backups and point-in-time recovery, replication for redundancy, read replicas to take analytical and read-heavy load off the primary, and — only where the volume genuinely demands it — partitioning to keep large tables manageable. Each step is added because the load or the risk warrants it, not because the architecture diagram looked more impressive with it.

How we approach Database Development

We design the data model before we build the schema, because the schema is the most expensive thing to get wrong. That means understanding the entities, the relationships between them and — critically — the access patterns: what is written often, what is read often, what has to be consistent and what can be eventually consistent. From those questions come the table shapes, the keys, the constraints and the indexes, and the honest answer to whether this is a relational problem or one of the far rarer cases where a document model genuinely fits better. We write the model down and explain the trade-offs, so the costly modelling decisions are made deliberately, in the open, while they are still cheap to change.

Then we build and migrate in controlled, reversible steps, with data integrity enforced in the database itself rather than hoped for in application code. Because the engineers who design the schema are the ones who tune and operate it, the unglamorous disciplines are present from the outset: the indexes match the queries, the migrations run forward and back cleanly, the backups are taken and a restore has actually been rehearsed. We would rather prove the model against realistic data volumes early than discover at scale that it was built for a demo.

How the engagement runs

We open with the data and its access patterns, not the framework. The first questions are what the entities are, how they relate, what gets written and read most often, what has to be strictly consistent and what can tolerate lag — because those answers fix the model, the storage engine and the indexing strategy. For a new build we design the schema from that and write the model down; for a rescue we start by measuring the running system, reading query plans and finding where the time and the risk actually are, rather than assuming. Either way, the expensive decisions are surfaced and argued early, while they are still cheap to change.

From there the work proceeds in controlled, reviewable steps. Schema changes go through the migration process — forward and reversible, applied identically in every environment — never hand-edited into production. Indexes are added and query fixes made against realistic data volumes and confirmed with query plans, not hoped to help. You see progress in measured improvements — a slow endpoint back to milliseconds, a query plan that no longer scans the whole table — not in a status deck. Backups, replication and monitoring are put in place as part of the build, so the database is operable and recoverable at every step rather than made safe in a fraught final week.

How we architect it

The heart of the architecture is the data model itself: a schema normalised to keep the data consistent, with denormalisation applied only where a specific, understood read pattern justifies the duplication and the cost of keeping it in sync. Integrity lives in the database — foreign keys, unique and check constraints — so the rules hold regardless of which application, job or person touched the data. We design the model to reflect the domain rather than the convenience of a particular ORM, because the schema outlives the framework in front of it and is far more expensive to change.

For scale, we add capacity in the order the load actually calls for, and no sooner. Correct indexing and query design come first, because they solve most performance problems outright and a poorly-modelled database does not get faster by being spread across more machines. Read replicas come next, taking read-heavy and analytical traffic off the primary while accepting the small replication lag that comes with them. Partitioning is reserved for genuinely large tables where it keeps queries and maintenance manageable. Sharding and abandoning relational guarantees are a last resort with a real cost, not a default — “we might need to scale” is rarely a good enough reason to give up transactions, joins and constraints before you have exhausted the far cheaper options.

Security

Access control is the first line: the database enforces least privilege, so an application connects with exactly the rights it needs and no more, and administrative access is separated from the day-to-day application role. Analytical and reporting workloads are pointed at replicas with read-only credentials rather than handed write access to the primary. The principle is that a compromised application credential or a mistaken query should be able to reach and damage as little as possible, because the blast radius of a database breach is the whole dataset.

Around that we build the rest of the posture deliberately. Data is encrypted in transit and at rest, and personally identifiable information is treated as the liability it is — identified, access-controlled, and where appropriate masked, minimised or held only as long as it is genuinely needed, with an eye to the obligations that come with holding personal data. Backups are encrypted and their restore rehearsed, because a backup you cannot safely restore is not protection. Secrets and connection strings are kept out of code and logs, and the monitoring that watches for slow queries and lock contention also surfaces the unusual access patterns that can be the first sign of something wrong. Security here is a property of how the data layer is designed, not a review bolted on before an audit.

Signs it’s time

  • You are starting a new system and the data model will be load-bearing — you want the schema, keys and constraints designed properly before a hundred features and a year of data are sitting on top of it
  • The application has become slow, and the usual culprits are in the database: missing indexes, N+1 query patterns, or queries scanning far more rows than they return
  • The schema has grown organically into something nobody wants to touch — duplicated data, columns whose meaning has drifted, and changes that feel too dangerous to make
  • You have no tested backup, no replication, or a migration process that is really someone hand-editing production and hoping, and the risk of it has finally caught up with you

Our working method

The organising principle is that the schema is the foundation everything is built on and the most expensive thing to change later, so it is the first thing we get right. We design the model before we build it, argue the relational-versus-NoSQL decision from your real access patterns rather than from fashion, and write the trade-offs down so the costly choices are made deliberately while they are still cheap. We treat a column split, a table rename or a new constraint on a large table as the careful, reversible, multi-step operations they are, and we enforce integrity in the database from the start rather than discovering after a corruption incident that application validation had a gap.

When the work is a rescue, the method is to measure before we touch anything: read the query plans, find where the time and the risk actually live, and fix causes rather than symptoms — an index and a killed N+1 loop instead of a bigger instance and a cache that hides the problem. And because the storage decision carries real, lasting consequences, part of the method is the honest call. We will tell you when PostgreSQL is the right default, when a document store genuinely fits, and when the case for NoSQL is really just an untested fear of scale — and we would rather talk you out of giving up relational guarantees you will miss than let you pay for that mistake later.

Technologies we build it with

Chosen per problem, not per fashion — this is the stack we most often reach for on this work.

How we deliver

  1. 01

    Discover

    We map the system, the constraints and the business it serves — including the parts nobody documented.

    Architecture brief

  2. 02

    Architect

    Decisions get made, written down and defended before a line of production code exists.

    Decision records

  3. 03

    Build

    Short cycles against working software. You see progress in the product, not in a status deck.

    Shipping increments

  4. 04

    Operate

    Monitoring, incident response and iteration. The system is alive, so the engagement is too.

    Runbooks & SLOs

What changes

  • A model that holds up

    A schema designed around how your data is actually read and written, with integrity enforced in the database, so it survives years of features and data instead of needing a rescue in eighteen months.

  • The slowness gone

    The database problems behind “the app is slow” — missing indexes, N+1 queries, full-table scans — found, understood and fixed, with the queries that mattered brought back to milliseconds.

  • Data you can trust and recover

    Constraints that keep bad data out, plus replication and backups with a restore that has actually been tested — so the data is both correct and recoverable when something goes wrong.

Industries we serve

Domain knowledge changes what gets built. A few of the sectors we know before the first meeting.

How pricing works

  • Fixed-scope build for a well-defined piece of data work — a new schema and data model, or a specific migration — quoted once the entities, the access patterns and the constraints are understood, so you are not paying for scope nobody has pinned down.
  • Focused audit or rescue for an existing database — a performance investigation into slow queries and missing indexes, a schema review, or a backup, replication and recovery assessment — priced by the scope of the problem, with the findings and the fix order as the deliverable.
  • Monthly senior engagement for evolving data work, where the model grows with the product and you want continuity on migrations, indexing, tuning and operational health rather than a one-off deliverable handed over and forgotten.

Typical timeline

  1. 01

    Modelling and assessment

    One to two weeks understanding the entities, relationships and access patterns and designing the model — or, for a rescue, measuring the running database, reading query plans and locating where the performance and risk problems actually are.

  2. 02

    Schema, migrations and integrity

    Building the schema with keys and constraints in place and a repeatable, reversible migration process — or, for an existing database, sequencing the safe migration steps towards the model it should have and enforcing the integrity that was missing.

  3. 03

    Indexing and performance tuning

    Adding the indexes that match your real queries, killing the N+1 patterns and slow scans, and confirming each fix against query plans and realistic data volumes rather than assuming it helped.

  4. 04

    Resilience and handover

    Replication, tested backups and point-in-time recovery in place, read replicas or partitioning where the load warrants it, monitoring on the metrics that predict trouble, and documentation so your team can operate and extend the database.

Why teams choose us for Database Development

  • Senior engineers only

    Data modelling and query tuning are easy to do adequately and hard to do well — the normalisation mistakes, the missing indexes, the migration that locks a live table, the N+1 nobody noticed until the data grew. The people designing your schema have run databases in production and watched these decisions age at scale. No juniors learning normalisation on the foundation your whole system will sit on.

  • We operate what we build

    We run the databases we design, so the parts that matter over years — tested backups, replication, safe migrations, monitoring — go in from the start rather than after the first failed restore or the first outage. We optimise for a data layer that stays correct and fast as the data grows, not for a schema that looks clean on an empty database and falls over at a million rows.

  • Honest about relational versus NoSQL

    The storage-engine decision has real, lasting consequences, and we make it on your access patterns rather than on what is fashionable. We will tell you when PostgreSQL is the right default, when a document store genuinely fits, and when “we might need to scale” is not a good enough reason to give up transactions, joins and constraints you will miss the moment your data gets complicated.

  • We fix causes, not symptoms

    When an application is slow, we find the database problem behind it by measuring — reading the query plan, not guessing — and fix the cause. That is usually the right index and a removed N+1, which is far cheaper and far more durable than a bigger instance or a cache layer that hides the real problem until it grows back worse.

How to engage us

Three ways to work with us on this — chosen to fit the problem, not our margin.

Related services

Part of Custom Software Development. Other work we do alongside this.

Common questions

Our app is slow — is that really a database problem?

More often than not, yes. A large share of “the app is slow” complaints trace back to the data layer: a query with no supporting index scanning the whole table, or an ORM quietly firing one query per row in a loop — the classic N+1 pattern. These are invisible on a small dataset in development and only bite once real data has accumulated. We diagnose it by measuring — reading the actual query plans and slow-query logs rather than guessing — and typically fix it with the right indexes and by removing the N+1 loops, which brings the system back to responsive without a rewrite or a bigger instance that only postpones the problem.

Should we use PostgreSQL or a NoSQL database like MongoDB?

For the large majority of systems, a relational database — and PostgreSQL is our sensible default — is the right answer, because transactions, joins and enforced constraints are genuine guarantees that keep your data correct, and giving them up has a real cost. A document store earns its place when the data is truly document-shaped and read as whole documents, not as an escape from doing data modelling. The reason we hear most often for reaching for NoSQL — “we might need to scale” — is usually the wrong one: PostgreSQL scales a very long way with good indexing and read replicas, and you should exhaust those cheaper options before abandoning relational guarantees you will miss. We make the call on your actual access patterns and show you the trade-offs.

Why does the schema design deserve so much effort up front?

Because the schema is the foundation everything else is built on, and it is the most expensive thing in the system to change once real data and real code depend on it. While the database is empty, splitting a table or renaming a column is trivial; once your application, reports and integrations all read and write it, the same change needs a careful migration, a deployment plan and a rollback path. Front-loading the modelling — the entities, relationships, keys and constraints — is therefore some of the cheapest, highest-leverage work in the whole project. An hour spent on the model now routinely saves weeks of painful migration and worked-around mistakes later.

Our database has become a mess — can you fix it without a full rewrite?

Usually, yes, and a big-bang rewrite is rarely the right first move. We start by mapping what is really there — the duplicated data, the columns whose meaning has drifted, the missing constraints — and designing the model it should have. Then we sequence the change into safe, reversible migration steps: introduce the new structure alongside the old, backfill and reconcile the data, move reads and writes across, and retire the old shape once nothing depends on it. That way the schema improves steadily while the system keeps running, rather than halting everything for a risky replacement.

How do you make sure we can actually recover the data if something goes wrong?

By treating recovery as something you prove, not something you assume. We set up automated backups with point-in-time recovery so you can restore to a moment just before an incident, and — the step most people skip — we actually rehearse a restore, because an untested backup is a hope rather than a plan and the day you discover it does not work is the worst possible day to find out. Alongside that we add replication for redundancy and to take read load off the primary, and monitoring on the signals that precede trouble: replication lag, lock contention and connection saturation. The goal is that the data is both correct while it lives and recoverable when something breaks.

Let’s talk about Database Development.

Tell us what you’re building or fixing. A senior engineer reads every enquiry and replies within a business day.

Two fields required. We reply to real enquiries — no list, no sequence.