고객 지원 문의

이메일로 답장드리며, 보통 이틀 안에 회신합니다.

Google reCAPTCHA가 이 제출을 악용 여부에 대해 확인하며, 이 과정에서 데이터가 Google로 전송됩니다. 스크립트는 이 양식을 열 때만 불러옵니다.

← 전체 글

Where the database schema actually lives

Everything this service does is in version control. The code is, anyway. The database it runs on is 32 tables, and 15 of them have no definition anywhere in the repository — no CREATE TABLE, no schema file, nothing. A dump restores all 32 with their structure intact; what the repository does not hold is a second, independent copy of that structure.

17 tables defined 15 defined nowhere 21 indexes created by code 11 only in the database 32 tables, 32 secondary indexes, 0 schema files

Those figures come from comparing the live database against 736 files and 5,671,710 characters of repository. They are not an estimate.

How half a schema goes missing

It is not carelessness, and that is what makes it worth writing down. Each individual step was reasonable.

Tables created before the current codebase existed were never written down, because at the time they were simply there. Tables added since arrived through migration scripts, and those scripts are in the repository — that is where the 17 definitions come from. Columns added later were added with a one-line ALTER TABLE typed at a prompt, because typing it was faster than writing a script for a change that took a second.

Indexes are the worst case. An index is added when something is slow, at the moment it is slow, and it fixes the problem immediately. There is no artefact left behind, nothing to review, nothing that fails when it is forgotten. Eleven of the thirty-two secondary indexes here exist for exactly that reason and are recorded nowhere.

The table that holds the counters themselves, 166,438 rows of them, is among the fifteen with no definition in the repository.

What actually breaks

Not much, until one specific moment: when the database is restored from a dump.

A dump carries the structure, so a straight restore is fine. The danger is any path that rebuilds a table rather than restoring it — moving to another machine, importing selected tables, recreating one from a script. The data comes back and the indexes quietly do not. Nothing fails. Queries return the right answers. They just take longer, and the cause is invisible, because the only record of what the index was is the machine that no longer has it.

That is the failure mode worth naming: a missing index does not produce an error, it produces a slower correct answer. There is no test for it, because the tests pass.

What we do about it in the meantime

The schema is not in the repository today, and that is the accurate description of the state. What is in place is narrower than a full fix, and it covers the case that actually costs something:

A dump before anything destructive, kept off the web root. A restore that has actually been performed at least once, rather than assumed. And a short list, checked after every restore, of the indexes that are known to exist only in the running database — because the machine can be asked what indexes it has, and the answer can be compared against the last time it was asked.

The general version applies to more than databases. If part of a system exists only in the running system, there is no second copy of it. The question worth asking about any piece of infrastructure is not whether it is backed up, but what could not be rebuilt from the repository if the machine went away. Here the answer is fifteen tables and eleven indexes, it took an afternoon to establish, and it is a list rather than an unknown.

Update: September 2026

Five days after this post went up, the working directory was committed: 26 files and 4,058 lines, among them eleven migration scripts written between 28 August and 1 September that until then existed only on the running machine. That was the easy half — code that had been written but never recorded.

The measurement was repeated on 1 September 2026 the same way: the live database against everything git tracks.

Tables4630 with a CREATE TABLE in the repository, 16 with none
Secondary indexes4332 created by code in the repository, 11 only in the running database
Migration scripts109in version control
30 tables defined 16 defined nowhere 32 indexes with a file 11 only in the database 46 tables, 43 secondary indexes, 0 schema files

The number that matters did not move. On 27 August eleven secondary indexes existed nowhere but in the running database. On 1 September there are still eleven. Eleven further indexes were added in between, and every one of them arrived with a migration script: the habit changed, the debt did not.

The eleven sit on five tables — the counters, the languages, the referrers, the exclusion list and the event log. All five predate the current codebase, which is exactly why nothing was ever written down for them. Writing a file now would mean reconstructing, from a running server, what was typed at a prompt years ago.

So the position from August stands. The schema is still not in the repository, and a restore that rebuilds a table instead of restoring it would still drop eleven indexes without a word. What changed is narrower: everything added since has left a file behind.

광고