Skip to main content
Database design and MySQL development

Databases designed so your reports still run in year five

The database is the part of a system that is hardest to change later and easiest to get wrong early. A good schema absorbs new requirements quietly for a decade. A poor one fights every change, slows down as data accumulates and eventually forces a rebuild of software that was otherwise fine.

Design decisions that show up years later

Almost every serious performance problem we are called in to fix has its origin in a decision made during the first fortnight of a project. A relationship modelled as one to one that later needed to be one to many. A status stored as free text so that reporting requires guessing. A field that should have kept history but only stores the current value, so nobody can answer what the price was in March.

None of those cause trouble at first. They surface in year two or three, when the business asks a reasonable question and the honest answer is that the data was never captured, or when a table reaches a size where a missing index turns a fast page into a thirty second wait. By then the fix means restructuring tables, migrating history and adjusting every part of the application that touched them.

We spend real time on the data model before development starts, and we ask about the future deliberately. What might this later belong to as well? Will you need to know what this used to be? What will you want to report on? How many of these will exist in five years? Those questions cost an afternoon and routinely save months.

Integrity enforced by the database

Applications have bugs, imports go wrong and somebody eventually writes a script that seemed safe at the time. The database is the last line of defence, and we use it properly. Foreign keys so a record cannot point at something that no longer exists. Unique constraints so the same reference cannot be created twice. Not null where a value is genuinely required. Sensible types rather than storing everything as text.

This matters more than it sounds. Data that has been allowed to become inconsistent is extremely difficult to repair afterwards, because by the time anyone notices, other records and reports have already been built on top of it. Preventing bad data is enormously cheaper than cleaning it.

Indexes chosen from real queries

Indexing is where most performance work begins and ends. An index makes reads faster and writes slightly slower, so the goal is the smallest set that covers the queries your system genuinely runs. We look at the actual query patterns rather than adding an index to every column, which is a surprisingly common approach that wastes storage and slows down every insert.

We also watch for the query patterns that scale badly, particularly code that issues one database call per row in a list. It looks harmless with twenty records and becomes catastrophic with twenty thousand, and it is by far the most frequent cause of an application that was fast on launch day and unusable a year later.

Migrations, imports and moving data safely

Changing a live database is a controlled operation. We use the Django migration system so that schema changes are versioned, repeatable and applied in the same order everywhere, rather than being typed by hand on a server and forgotten. Before anything runs against production, it has run against a copy.

Data imports from spreadsheets or older systems are treated as their own small project, because real data always contains duplicates, inconsistent spellings, dates in several formats and notes in the wrong column. We run the import repeatedly against copies, produce a report of everything that could not be matched, and agree with you how those cases should be handled before the live migration happens.

Backups that have actually been restored

A backup nobody has restored is a hope rather than a plan. We configure automated backups, keep copies away from the server they came from, and test restoration so we know how long it takes and that it works. We also think about retention, because the most common data loss we see is not a failed disk, it is somebody deleting something in error and nobody noticing for three weeks.

Services

  • Schema design for new applications
  • Review of an existing database
  • Index analysis and query tuning
  • Slow query investigation
  • Data migration and cleaning
  • Reporting structures and summaries
  • Archiving strategies for large tables
  • Backup configuration and restore testing
  • Replication and read scaling where needed

Technologies

MySQLSQLDjango ORM RedisSQLiteMigrations

Database questions

Very often, yes, and usually because of a small number of specific queries rather than the database as a whole. We measure before changing anything, identify the queries responsible and fix those. Guessing at performance problems wastes money and frequently makes things slower.

It is mature, widely supported, well understood by a large number of developers, available from every hosting provider and entirely capable of the workloads most business systems produce. For the applications we build it is a sensible default rather than a compromise, and it keeps your options open if you ever change supplier.

Far more than most businesses generate, provided the schema and indexes are right. Problems attributed to data volume are usually caused by missing indexes, queries that fetch far more than they need, or reports run against live tables when they should be run against summaries.

Usually. Even where a vendor is unhelpful there is often a database export, a reporting interface or a file format we can work with. We have extracted data from some genuinely awkward systems, and the first step is always an assessment of what is realistically recoverable.

Worried about the data underneath your software?

A database review will tell you what condition it is in, where the risks sit and what would make the biggest difference, before anything is changed.