Concepts / Writing Objects to a File

Writing Objects to a File

A version number in a file's first line tells a program which layout rules were used, and a higher number signals a newer format.

  • Programming

When a Saved File Outlives Its Program

Imagine a drawing program that saves its pictures to a file. Later, the same program is updated, and the new version may store its information a little differently than the old one did. Will the new version still open the files the old version saved? The honest answer is that it depends on how the data was represented in the file from the beginning.

This article looks at two problems that appear when a file is tied too closely to the exact layout a program uses today. It also looks at two design habits that reduce those problems: storing a version number inside the file, and choosing a format that a person can read and understand. Along the way you will trace what happens to the values in a file as the program changes, so the problems are visible rather than abstract.

Following Values Through a Saved File

A file written without a version number is a long sequence of values. Nothing in the file names each value. Its meaning comes entirely from its position: the reader takes the values in the same order the writer produced them. The reader is only correct if it assumes the same positions the writer used.

The example below is an invented illustration, not one from the source. Version 1 of a program saves two values in this order: a width at position 0 and a height at position 1. Version 2 adds a depth value and places it between them, so the layout becomes width, depth, height. Before you look at the diagram, decide what the old reader will see when it reads position 1.

What do you think happens?

Version 2 writes width, depth, height in that order. A version 1 reader expects height at position 1. What value does it read as height?

  • The height, 480, because the reader always finds it

  • The depth, 16, because that value now sits at position 1

  • The width, 640, because the reader starts again from the top

Reveal answer
Answer:

The depth, 16, because that value now sits at position 1

The reader does not look for labels. It counts positions. Position 1 now holds depth, so the old reader treats 16 as the height. The real height has moved to position 2 and is never read as height.

width640width640height480depth16old reader's height16height480
When a depth field is inserted between width and height, which position holds each value, and what does the old reader pull from position 1?

The diagram shows the cause of the failure. Nothing in the file was damaged. The height value still exists at position 2, and the depth value sits exactly where the old reader looks for height. Only the assumed positions have changed, so the old reader quietly reads the wrong number. Because no error is raised, this kind of failure can go unnoticed for a long time.

Why Fixed Positions Make Files Fragile

A file that depends on exact program internals is fragile because its meaning is tied to a specific set of rules inside the program. The file itself does not say which rules were used. If those rules change, the same bytes can be interpreted in a new and incorrect way.

The source gives a concrete case. Suppose the program adds a new property to curves, so that it now expects an extra value for each curve. Every older file becomes unreadable, unless the user happens to remember which version of the program created that file. The data is not damaged in any physical sense. What has changed is the set of rules needed to interpret it.

The Version Number as a Gatekeeper

A well-designed data file begins with a small header that describes the file itself. In the format discussed in the source, the first line names the program that created the file. This gives the program a quick test. When a user picks a file to open, the program can read the first word and confirm the file is the correct type before it tries to read anything else.

The same first line also carries a version number. In the source example it is 1.0. The version answers a different question from the program name. It asks which rules were used to write the rest of the file. Each time the layout changes, the number changes with it, and a higher number means a newer layout.

Read first lineProgram name matchesWrong file typeRead rest with knownrulesRead version numberVersion recognizedAsk for a newerprogramnoyesyesno
After the program reads the version number in the header, does it accept the file, reject it, or explain that a newer version is needed, and which branch is taken for a known version versus an unknown one?

Follow the diagram for a file from a known version. The program confirms the type, reads the version, recognizes it, and reads the rest of the file using the rules for that version. Now follow a file from a newer version. The program still confirms the type and reads the version, but it does not recognize the version. The source says the program should explain that a newer version is needed rather than guess at the contents. A guess could produce the wrong values silently, which is exactly the failure shown earlier.

Best practice

Store the version number inside the file itself, not only in documentation. A reader that sees a version it does not understand can stop and report the problem, which is far safer than interpreting the file under the wrong rules.

Labels That Make a File Readable

A human-readable format uses clear labels so that a person looking at the file can tell what each piece of data means. In the source's example, a line in the file begins with the word background and is followed by three numbers. Those numbers are the red, green, and blue components of the color. The label removes the guesswork, so the line explains itself.

backgroundfield name255red component128green component0blue component
Which token is the field name, and which tokens are the values and what each one means? The numbers are illustrative values, not taken from the source.

The same idea appears in the curve data. Each curve is wrapped between the markers startcurve and endcurve. Inside those markers the file holds the curve's color, its symmetry property, and the xy-coordinates of each point. Because the structure is spelled out in words, a reader can follow the file from top to bottom without running a program.

Readability has practical value in three ways. Files in this format can be created or edited by hand, and the source notes that the example file was in fact written in a text editor rather than produced by the program. A readable file is easier to debug, because a missing marker or a misplaced number is visible on the page. And labeled data is easier to extend, since a new option can be added as a new labeled line without forcing every reader to memorize a fixed position.

AspectPosition-based layoutLabeled, readable format
Meaning of a valueDepends on its position in the fileStated by a label next to the value
Adding a new valueCan shift every later value and break old readersCan be added as a new labeled item
Editing by handHard, since a person must count positionsPossible in a text editor
Finding a mistakeA misplaced value looks like any other valueA missing marker or misplaced label is visible

Two ways to lay out the same information in a file.

Mistakes When Designing a Data File

  • Common mistake

    Assuming a file is safe because nothing has been lost

    What it looks like

    The old reader in the earlier example reads 16 as the height, yet no error is raised and the file still looks complete.

    Why it fails

    A file can be fully intact and still be misread. Meaning depends on the rules used to interpret the bytes, not only on whether the bytes exist.

    Do this instead

    Record which layout rules were used, so a reader can check whether it understands them before reading.

  • Common mistake

    Leaving out a version number because the file seems simple

    What it looks like

    A program saves a list of values with no header. A later update adds one value in the middle of the list.

    Why it fails

    Without a version, a reader has no way to tell which layout produced the file, so older and newer files look identical.

    Do this instead

    Start every data file with a small header that names the file type and carries a version number.

  • Common mistake

    Guessing at the contents of a file with a newer version

    Why it fails

    The source says a program that meets a version it does not understand should explain that a newer version is needed rather than guess. A guess can produce wrong values with no warning.

    Do this instead

    Check the version number first, and stop with a clear message when the version is not recognized.

  • Common mistake

    Using unlabeled values when a person may need to edit the file

    Why it fails

    Unlabeled numbers force the reader to remember their meaning and their order. Mistakes are hard to spot and hard to fix by hand.

    Do this instead

    Give each piece of data a clear label, as in the background line that begins with its name.

Checking Your Understanding

MEDIUM

A program saves a file with the header line 'drawing 1.0' followed by a list of values in fixed positions. Version 2 inserts a new value at position 0 and moves every other value one place down. Without changing the program, explain what a version 1 reader will read at each position, and state what the version 2 program should do when it opens a version 2 file that it does not recognize.

Hints
  • Compare the position of each value in version 1 with its position in version 2.
  • Think about what the reader assumes about position when the header says 1.0.
  • The source says an unrecognized version should lead to an explanation, not a guess.

Key Takeaways

  1. 1
    A file that relies on fixed positions is fragile, because its meaning depends on the exact rules the program used when it wrote the file.
  2. 2
    A version number in the first line tells a program which layout rules were used, and a higher number signals a newer format.
  3. 3
    A program that meets a version it does not understand should explain that a newer version is needed rather than guess at the contents.
  4. 4
    Labeled, human-readable formats are easier to create, edit, debug, and extend, because each value carries its own meaning.

At a glance

  • A file that relies on fixed positions is fragile, because its meaning depends on the exact rules the program used when it wrote the file.
  • A version number in the first line tells a program which layout rules were used, and a higher number signals a newer format.
  • A program that meets a version it does not understand should explain that a newer version is needed rather than guess at the contents.
  • Labeled, human-readable formats are easier to create, edit, debug, and extend, because each value carries its own meaning.